Lint fixes and multiple bids test.

This commit is contained in:
tecnovert 2020-11-30 16:29:40 +02:00
parent 4913ac7a2b
commit 7f4be161d7
14 changed files with 267 additions and 245 deletions

View File

@ -48,7 +48,7 @@ jobs:
before_script:
script:
- PYTHONWARNINGS="ignore" flake8 --ignore=E501,F841,W503 --exclude=basicswap/contrib,messages_pb2.py,.eggs
- codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=tests/lint/spelling.ignore-words.txt -S .git,.eggs,gitianpubkeys,*.pyc,basicswap/contrib
- codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=tests/lint/spelling.ignore-words.txt -S .git,.eggs,gitianpubkeys,*.pyc,*basicswap/contrib,*mnemonics.py
after_success:
- echo "End lint"
- stage: test

View File

@ -41,7 +41,6 @@ from .util import (
toWIF,
getKeyID,
make_int,
dumpj
)
from .chainparams import (
chainparams,
@ -81,8 +80,6 @@ from .db import (
from .explorers import ExplorerInsight, ExplorerBitAps, ExplorerChainz
import basicswap.config as cfg
from .base import BaseApp
from .ecc_util import (
b2i, i2b)
MIN_OFFER_VALID_TIME = 60 * 10
@ -359,7 +356,7 @@ class WatchedOutput():
class WatchedTransaction():
# Watch for presense in mempool (getrawtransaction)
# Watch for presence in mempool (getrawtransaction)
def __init__(self, bid_id, txid_hex, tx_type, swap_type):
self.bid_id = bid_id
self.txid_hex = txid_hex
@ -1281,7 +1278,7 @@ class BasicSwap(BaseApp):
bid.xmr_b_lock_tx = stx
else:
bid.txns[stx.tx_type] = stx
#self.log.warning('Unknown transaction type: {}'.format(stx.tx_type))
def getXmrBid(self, bid_id, sent=False):
self.mxDB.acquire()
try:
@ -1524,14 +1521,10 @@ class BasicSwap(BaseApp):
ro = self.callrpc('smsgsend', [bid_addr, offer.addr_from, payload_hex, False, msg_valid, False, options])
xmr_swap.bid_msg_id3 = bytes.fromhex(ro['msgid'])
bid = Bid(
bid_id=xmr_swap.bid_id,
offer_id=offer_id,
amount=msg_buf.amount,
#pkhash_buyer=msg_buf.pkhash_buyer,
#proof_address=msg_buf.proof_address,
created_at=bid_created_at,
contract_count=xmr_swap.contract_count,
amount_to=(msg_buf.amount * offer.rate) // COIN,
@ -2358,22 +2351,19 @@ class BasicSwap(BaseApp):
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)
# bid.setState(BidStates.SWAP_DELAYING)
session.commit()
elif state == BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED:
#if bid.was_sent and TxTypes.XMR_SWAP_B_LOCK not in bid.txns:
if bid.was_sent and bid.xmr_b_lock_tx is None:
return rv
found_tx = ci_to.findTxB(xmr_swap.vkbv, xmr_swap.pkbs, bid.amount_to, ci_to.blocks_confirmed, xmr_swap.b_restore_height)
if found_tx is not None:
#if TxTypes.XMR_SWAP_B_LOCK not in bid.txns:
if bid.xmr_b_lock_tx is None:
b_lock_tx_id = bytes.fromhex(found_tx['txid'])
#bid.txns[TxTypes.XMR_SWAP_B_LOCK] = SwapTx(
bid.xmr_b_lock_tx = SwapTx(
bid_id=bid_id,
tx_type=TxTypes.XMR_SWAP_B_LOCK,
@ -2400,7 +2390,6 @@ class BasicSwap(BaseApp):
# TODO: Use explorer to get tx / block hash for getrawtransaction
pass
elif state == BidStates.XMR_SWAP_SCRIPT_TX_REDEEMED:
#txid_hex = bid.txns[TxTypes.XMR_SWAP_B_LOCK].spend_txid.hex()
txid_hex = bid.xmr_b_lock_tx.spend_txid.hex()
found_tx = ci_to.findTxnByHash(txid_hex)
@ -2688,9 +2677,7 @@ class BasicSwap(BaseApp):
bid.setState(BidStates.XMR_SWAP_SCRIPT_TX_REDEEMED) # TODO: Wait for confirmation?
if not bid.was_received:
#rv = True # Remove from swaps_in_progress
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)
@ -2700,6 +2687,7 @@ class BasicSwap(BaseApp):
self.log.warning('Coin a lock tx spend ignored due to bid state for bid {}'.format(bid_id.hex()))
elif spending_txid == xmr_swap.a_lock_refund_tx_id:
self.log.debug('Coin a lock tx spent by lock refund tx.')
pass
else:
self.setBidError(bid.bid_id, bid, 'Unexpected txn spent coin a lock tx: {}'.format(spend_txid_hex), save_bid=False)
@ -2751,7 +2739,6 @@ class BasicSwap(BaseApp):
self.log.info('Recovering xmr swap chain B lock tx for bid %s in %d seconds', bid_id.hex(), delay)
self.createEventInSession(delay, EventTypes.RECOVER_XMR_SWAP_LOCK_TX_B, bid_id, session)
else:
#rv = True # Remove from swaps_in_progress
bid.setState(BidStates.XMR_SWAP_FAILED_REFUNDED)
if bid.was_received:
@ -3181,8 +3168,8 @@ class BasicSwap(BaseApp):
now = int(time.time())
offer, xmr_offer = self.getXmrOffer(bid.offer_id, sent=True)
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(offer and offer.was_sent), '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)
@ -3262,7 +3249,6 @@ class BasicSwap(BaseApp):
if not ci_from.verifyPubkey(xmr_swap.pkarl):
raise ValueError('Invalid pubkey.')
#bid.setState(BidStates.BID_ACCEPTED)
bid.setState(BidStates.SWAP_DELAYING)
self.saveBidInSession(bid.bid_id, bid, session, xmr_swap)
@ -3301,8 +3287,6 @@ class BasicSwap(BaseApp):
bid_id=bid_id,
offer_id=offer_id,
amount=bid_data.amount,
#pkhash_buyer=bid_data.pkhash_buyer,
created_at=msg['sent'],
amount_to=(bid_data.amount * offer.rate) // COIN,
expire_at=msg['sent'] + bid_data.time_valid,
@ -3563,17 +3547,27 @@ class BasicSwap(BaseApp):
if bid.debug_ind == DebugTypes.CREATE_INVALID_COIN_B_LOCK:
self.log.debug('XMR bid %s: Debug %d - Reducing lock b txn amount by 10%%.', bid_id.hex(), bid.debug_ind)
bid.amount_to -= int(bid.amount_to * 0.1)
try:
b_lock_tx_id = ci_to.publishBLockTx(xmr_swap.pkbv, xmr_swap.pkbs, bid.amount_to, xmr_offer.b_fee_rate)
except Exception as ex:
self.log.error('publishBLockTx failed for bid {} with error {}'.format(bid_id.hex(), str(ex)))
if 'not enough unlocked money' in str(ex):
delay = random.randrange(self.min_delay_auto_accept, self.max_delay_auto_accept) # TODO: New delay range
self.log.info('Retrying 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)
else:
self.setBidError(bid_id, bid, 'publishBLockTx failed: ' + str(ex), save_bid=False)
self.saveBidInSession(bid_id, bid, session, xmr_swap)
return
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(
#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)
#bid.txns[TxTypes.XMR_SWAP_B_LOCK] = xmr_b_lock_tx
self.saveBidInSession(bid_id, bid, session, xmr_swap)
@ -3688,21 +3682,14 @@ class BasicSwap(BaseApp):
assert(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, 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
#txn = bid.txns[TxTypes.XMR_SWAP_B_LOCK]
#print('[rm] TxTypes.XMR_SWAP_B_LOCK', txn.bid_id.hex())
#txn.spend_txid = txid
#bid.txns[TxTypes.XMR_SWAP_B_LOCK].spend_txid = txid
# TODO: Why does using bid.txns error here?
self.saveBidInSession(bid_id, bid, session, xmr_swap)
# Update copy of bid in swaps_in_progress
self.swaps_in_progress[bid_id] = (bid, offer)
@ -3730,11 +3717,7 @@ class BasicSwap(BaseApp):
assert(kbsl is not None)
kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_xmr=True)
vkbs = ci_to.sumKeys(kbsl, kbsf)
Kbs_test = ci_to.getPubkey(vkbs)
print('Kbs_test', Kbs_test.hex())
print('Kbs', xmr_swap.pkbsf.hex())
address_to = ci_to.getMainWalletAddress()
@ -3747,7 +3730,6 @@ class BasicSwap(BaseApp):
# Update copy of bid in swaps_in_progress
self.swaps_in_progress[bid_id] = (bid, offer)
def processXmrBidCoinALockSigs(self, msg):
# Leader processing MSG3L
self.log.debug('Processing xmr coin a follower lock sigs msg %s', msg['msgid'])

View File

@ -310,4 +310,3 @@ class XmrSplitData(Base):
msg_sequence = sa.Column(sa.Integer)
dleag = sa.Column(sa.LargeBinary)
created_at = sa.Column(sa.BigInteger)

View File

@ -5,7 +5,6 @@
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import os
import json
import time
import struct
import traceback
@ -17,7 +16,6 @@ from jinja2 import Environment, PackageLoader
from . import __version__
from .util import (
make_int,
dumpj,
)
from .chainparams import (
@ -26,9 +24,6 @@ from .chainparams import (
)
from .basicswap import (
SwapTypes,
BidStates,
TxStates,
TxTypes,
strOfferState,
strBidState,
strTxState,
@ -45,6 +40,12 @@ from .js_server import (
js_sentbids,
js_index,
)
from .ui import (
PAGE_LIMIT,
inputAmount,
describeBid,
setCoinFilter,
)
def format_timestamp(value):
@ -53,7 +54,6 @@ def format_timestamp(value):
env = Environment(loader=PackageLoader('basicswap', 'templates'))
env.filters['formatts'] = format_timestamp
PAGE_LIMIT = 50
def getCoinName(c):
@ -89,141 +89,6 @@ def listExplorerActions(swap_client):
return actions
def listBidStates():
rv = []
for s in BidStates:
rv.append((int(s), strBidState(s)))
return rv
def getTxIdHex(bid, tx_type, prefix):
if tx_type == TxTypes.ITX:
obj = bid.initiate_tx
elif tx_type == TxTypes.PTX:
obj = bid.participate_tx
else:
return 'Unknown Type'
if not obj:
return 'None'
if not obj.txid:
return 'None'
return obj.txid.hex() + prefix
def getTxSpendHex(bid, tx_type):
if tx_type == TxTypes.ITX:
obj = bid.initiate_tx
elif tx_type == TxTypes.PTX:
obj = bid.participate_tx
else:
return 'Unknown Type'
if not obj:
return 'None'
if not obj.spend_txid:
return 'None'
return obj.spend_txid.hex() + ' {}'.format(obj.spend_n)
def validateAmountString(amount):
if type(amount) != str:
return
ar = amount.split('.')
if len(ar) > 1 and len(ar[1]) > 8:
raise ValueError('Too many decimal places in amount {}'.format(amount))
def inputAmount(amount_str):
validateAmountString(amount_str)
return make_int(amount_str)
def setCoinFilter(form_data, field_name):
if field_name not in form_data:
return -1
coin_type = int(form_data[field_name][0])
if coin_type == -1:
return -1
try:
return Coins(coin_type)
except Exception:
raise ValueError('Unknown Coin Type {}'.format(str(field_name)))
def describeBid(swap_client, bid, offer, edit_bid, show_txns):
coin_from = Coins(offer.coin_from)
coin_to = Coins(offer.coin_to)
ci_from = swap_client.ci(coin_from)
ci_to = swap_client.ci(coin_to)
ticker_from = swap_client.getTicker(coin_from)
ticker_to = swap_client.getTicker(coin_to)
if bid.state == BidStates.BID_SENT:
state_description = 'Waiting for seller to accept.'
elif bid.state == BidStates.BID_RECEIVED:
state_description = 'Waiting for seller to accept.'
elif bid.state == BidStates.BID_ACCEPTED:
if not bid.initiate_tx:
state_description = 'Waiting for seller to send initiate tx.'
else:
state_description = 'Waiting for initiate tx to confirm.'
elif bid.state == BidStates.SWAP_INITIATED:
state_description = 'Waiting for participate txn to be confirmed in {} chain'.format(ticker_to)
elif bid.state == BidStates.SWAP_PARTICIPATING:
state_description = 'Waiting for initiate txn to be spent in {} chain'.format(ticker_from)
elif bid.state == BidStates.SWAP_COMPLETED:
state_description = 'Swap completed'
if bid.getITxState() == TxStates.TX_REDEEMED and bid.getPTxState() == TxStates.TX_REDEEMED:
state_description += ' successfully'
else:
state_description += ', ITX ' + strTxState(bid.getITxState()) + ', PTX ' + strTxState(bid.getPTxState())
elif bid.state == BidStates.SWAP_TIMEDOUT:
state_description = 'Timed out waiting for initiate txn'
elif bid.state == BidStates.BID_ABANDONED:
state_description = 'Bid abandoned'
elif bid.state == BidStates.BID_ERROR:
state_description = bid.state_note
else:
state_description = ''
data = {
'amt_from': ci_from.format_amount(bid.amount),
'amt_to': ci_to.format_amount((bid.amount * offer.rate) // ci_from.COIN()),
'ticker_from': ticker_from,
'ticker_to': ticker_to,
'bid_state': strBidState(bid.state),
'state_description': state_description,
'itx_state': strTxState(bid.getITxState()),
'ptx_state': strTxState(bid.getPTxState()),
'offer_id': bid.offer_id.hex(),
'addr_from': bid.bid_addr,
'addr_fund_proof': bid.proof_address,
'created_at': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(bid.created_at)),
'expired_at': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(bid.expire_at)),
'was_sent': 'True' if bid.was_sent else 'False',
'was_received': 'True' if bid.was_received else 'False',
'initiate_tx': getTxIdHex(bid, TxTypes.ITX, ' ' + ticker_from),
'initiate_conf': 'None' if (not bid.initiate_tx or not bid.initiate_tx.conf) else bid.initiate_tx.conf,
'participate_tx': getTxIdHex(bid, TxTypes.PTX, ' ' + ticker_to),
'participate_conf': 'None' if (not bid.participate_tx or not bid.participate_tx.conf) else bid.participate_tx.conf,
'show_txns': show_txns,
}
if edit_bid:
data['bid_state_ind'] = int(bid.state)
data['bid_states'] = listBidStates()
if show_txns:
data['initiate_tx_refund'] = 'None' if not bid.initiate_txn_refund else bid.initiate_txn_refund.hex()
data['participate_tx_refund'] = 'None' if not bid.participate_txn_refund else bid.participate_txn_refund.hex()
data['initiate_tx_spend'] = getTxSpendHex(bid, TxTypes.ITX)
data['participate_tx_spend'] = getTxSpendHex(bid, TxTypes.PTX)
return data
def html_content_start(title, h2=None, refresh=None):
content = '<!DOCTYPE html><html lang="en">\n<head>' \
+ '<meta charset="UTF-8">' \

View File

@ -362,7 +362,6 @@ class BTCInterface(CoinInterface):
tx.nVersion = self.txVersion()
tx.vin.append(CTxIn(COutPoint(tx_lock_refund_hash_int, locked_n), nSequence=0))
#pubkeyhash = hash160(Kal)
tx.vout.append(self.txoType(locked_coin, CScript([OP_0, pkh_refund_to])))
witness_bytes = len(script_lock_refund)
@ -591,9 +590,11 @@ class BTCInterface(CoinInterface):
assert_cond(len(tx.vout) == 1, 'tx doesn\'t have one output')
# Destination doesn't matter to the follower
#p2wpkh = CScript([OP_0, hash160(Kal)])
#locked_n = findOutput(tx, p2wpkh)
#assert_cond(locked_n is not None, 'Output not found in lock refund spend tx')
'''
p2wpkh = CScript([OP_0, hash160(Kal)])
locked_n = findOutput(tx, p2wpkh)
assert_cond(locked_n is not None, 'Output not found in lock refund spend tx')
'''
tx_value = tx.vout[0].nValue
fee_paid = prevout_value - tx_value
@ -711,8 +712,6 @@ class BTCInterface(CoinInterface):
def signTxWithWallet(self, tx):
rv = self.rpc_callback('signrawtransactionwithwallet', [tx.hex()])
#return FromHex(tx, rv['hex'])
return bytes.fromhex(rv['hex'])
def publishTx(self, tx):
@ -798,7 +797,6 @@ class BTCInterface(CoinInterface):
def recoverEncKey(self, esig, sig, K):
return ecdsaotves_rec_enc_key(K, esig, sig[:-1]) # Strip sighash type
#return otves.RecoverEncKey(esig, sig[:-1], K) # Strip sighash type
def getTxVSize(self, tx, add_bytes=0, add_witness_bytes=0):
wsf = self.witnessScaleFactor()
@ -845,12 +843,6 @@ class BTCInterface(CoinInterface):
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())]])
'''
bech32_prefix = chainparams[self.coin_type()][self._network]['hrp']
address = segwit_addr.encode(bech32_prefix, 0, list(dest_script[2:]))
print('[rm] address', address)
utxos = self.rpc_callback('scantxoutset', ['start', ['addr({})'.format(address)]])
'''
chain_height = utxos['height']
rv = []
for utxo in utxos['unspents']:
@ -871,7 +863,6 @@ class BTCInterface(CoinInterface):
return rv
def testBTCInterface():
print('testBTCInterface')
script_bytes = bytes.fromhex('6382012088a820aaf125ff9a34a74c7a17f5e7ee9d07d17cc5e53a539f345d5f73baa7e79b65e28852210224019219ad43c47288c937ae508f26998dd81ec066827773db128fd5e262c04f21039a0fd752bd1a2234820707852e7a30253620052ecd162948a06532a817710b5952ae670114b2755221038689deba25c5578e5457ddadbaf8aeb8badf438dc22f540503dbd4ae10e14f512103c9c5d5acc996216d10852a72cd67c701bfd4b9137a4076350fd32f08db39575552ae68')
@ -954,6 +945,5 @@ def testBTCInterface():
print('Passed.')
if __name__ == "__main__":
testBTCInterface()

View File

@ -8,6 +8,7 @@
from .interface_btc import BTCInterface
from .chainparams import Coins
class LTCInterface(BTCInterface):
@staticmethod
def coin_type():

View File

@ -8,6 +8,7 @@
from .interface_btc import BTCInterface
from .chainparams import Coins
class NMCInterface(BTCInterface):
@staticmethod
def coin_type():

View File

@ -151,6 +151,7 @@ class XMRInterface(CoinInterface):
return self.encodePubkey(edf.edwards_add(Ka_d, Kb_d))
def publishBLockTx(self, Kbv, Kbs, output_amount, feerate):
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
shared_addr = xmr_util.encode_address(Kbv, Kbs)
@ -158,11 +159,18 @@ class XMRInterface(CoinInterface):
params = {'destinations': [{'amount': output_amount, 'address': shared_addr}]}
rv = self.rpc_wallet_cb('transfer', params)
logging.info('publishBLockTx %s to address_b58 %s', rv['tx_hash'], shared_addr)
tx_hash = bytes.fromhex(rv['tx_hash'])
return bytes.fromhex(rv['tx_hash'])
# Debug
for i in range(10):
params = {'out': True, 'pending': True, 'failed': True, 'pool': True, }
rv = self.rpc_wallet_cb('get_transfers', params)
logging.info('[rm] get_transfers {}'.format(dumpj(rv)))
time.sleep(1)
return tx_hash
def findTxB(self, kbv, Kbs, cb_swap_value, cb_block_confirmed, restore_height):
#Kbv_enc = self.encodePubkey(self.pubkey(kbv))
Kbv = self.getPubkey(kbv)
address_b58 = xmr_util.encode_address(Kbv, Kbs)
@ -176,7 +184,6 @@ class XMRInterface(CoinInterface):
'restore_height': restore_height,
'filename': address_b58,
'address': address_b58,
#'viewkey': b2h(intToBytes32_le(kbv)),
'viewkey': b2h(kbv_le),
}
@ -220,7 +227,7 @@ class XMRInterface(CoinInterface):
params = {
'filename': address_b58,
'address': address_b58,
'viewkey': b2h(intToBytes32_le(kbv)),
'viewkey': b2h(kbv[::-1]),
'restore_height': restore_height,
}
self.rpc_wallet_cb('generate_from_keys', params)

View File

@ -4,45 +4,31 @@
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import os
import json
import time
import struct
import traceback
import threading
import urllib.parse
from . import __version__
from .util import (
COIN,
format8,
make_int,
dumpj,
)
from .chainparams import (
chainparams,
Coins,
)
from .util import format8
from .basicswap import (
SwapTypes,
BidStates,
TxStates,
TxTypes,
strOfferState,
strBidState,
strTxState,
getLockName,
SEQUENCE_LOCK_TIME,
ABS_LOCK_TIME,
)
from .ui import (
PAGE_LIMIT,
inputAmount,
describeBid,
setCoinFilter,
)
def js_error(self, error_str):
error_str_json = json.dumps({'error': error_str})
return bytes(error_str_json, 'UTF-8')
def js_wallets(self, url_split, post_string):
return bytes(json.dumps(self.server.swap_client.getWalletsInfo()), 'UTF-8')
def js_offers(self, url_split, post_string, sent=False):
if len(url_split) > 3:
if url_split[3] == 'new':
@ -82,27 +68,28 @@ def js_offers(self, url_split, post_string, sent=False):
filters['limit'] = int(post_data[b'limit'][0])
assert(filters['limit'] > 0 and filters['limit'] <= PAGE_LIMIT), 'Invalid limit'
ci_from = self.server.swap_client.ci(o.coin_from)
ci_to = self.server.swap_client.ci(o.coin_to)
offers = self.server.swap_client.listOffers(sent, filters)
rv = []
for o in offers:
ci_from = self.server.swap_client.ci(o.coin_from)
ci_to = self.server.swap_client.ci(o.coin_to)
rv.append({
'offer_id': o.offer_id.hex(),
'created_at': time.strftime('%Y-%m-%d %H:%M', time.localtime(o.created_at)),
'coin_from': ci_from.coin_name(),
'coin_to': ci_to.coin_name(),
'amount_from': ci_from.format_amount(o.amount_from),
'amount_to': ci_to.format_amount((o.amount_from * o.rate) // COIN),
'rate': format8(o.rate)
'amount_to': ci_to.format_amount((o.amount_from * o.rate) // ci_from.COIN()),
'rate': ci_to.format_amount(o.rate)
})
return bytes(json.dumps(rv), 'UTF-8')
def js_sentoffers(self, url_split, post_string):
return self.js_offers(url_split, post_string, True)
def js_bids(self, url_split, post_string):
swap_client = self.server.swap_client
if len(url_split) > 3:
@ -153,8 +140,10 @@ def js_bids(self, url_split, post_string):
'bid_state': strBidState(b[4])
} for b in bids]), 'UTF-8')
def js_sentbids(self, url_split, post_string):
return bytes(json.dumps(self.server.swap_client.listBids(sent=True)), 'UTF-8')
def js_index(self, url_split, post_string):
return bytes(json.dumps(self.server.swap_client.getSummary()), 'UTF-8')

13
basicswap/network.py Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2020 tecnovert
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
'''
TODO:
'''
class Peer:
pass

158
basicswap/ui.py Normal file
View File

@ -0,0 +1,158 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020 tecnovert
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import time
from .util import (
make_int,
)
from .chainparams import (
Coins,
)
from .basicswap import (
BidStates,
TxStates,
TxTypes,
strBidState,
strTxState,
)
PAGE_LIMIT = 50
def validateAmountString(amount):
if type(amount) != str:
return
ar = amount.split('.')
if len(ar) > 1 and len(ar[1]) > 8:
raise ValueError('Too many decimal places in amount {}'.format(amount))
def inputAmount(amount_str):
validateAmountString(amount_str)
return make_int(amount_str)
def setCoinFilter(form_data, field_name):
if field_name not in form_data:
return -1
coin_type = int(form_data[field_name][0])
if coin_type == -1:
return -1
try:
return Coins(coin_type)
except Exception:
raise ValueError('Unknown Coin Type {}'.format(str(field_name)))
def getTxIdHex(bid, tx_type, prefix):
if tx_type == TxTypes.ITX:
obj = bid.initiate_tx
elif tx_type == TxTypes.PTX:
obj = bid.participate_tx
else:
return 'Unknown Type'
if not obj:
return 'None'
if not obj.txid:
return 'None'
return obj.txid.hex() + prefix
def getTxSpendHex(bid, tx_type):
if tx_type == TxTypes.ITX:
obj = bid.initiate_tx
elif tx_type == TxTypes.PTX:
obj = bid.participate_tx
else:
return 'Unknown Type'
if not obj:
return 'None'
if not obj.spend_txid:
return 'None'
return obj.spend_txid.hex() + ' {}'.format(obj.spend_n)
def listBidStates():
rv = []
for s in BidStates:
rv.append((int(s), strBidState(s)))
return rv
def describeBid(swap_client, bid, offer, edit_bid, show_txns):
coin_from = Coins(offer.coin_from)
coin_to = Coins(offer.coin_to)
ci_from = swap_client.ci(coin_from)
ci_to = swap_client.ci(coin_to)
ticker_from = swap_client.getTicker(coin_from)
ticker_to = swap_client.getTicker(coin_to)
if bid.state == BidStates.BID_SENT:
state_description = 'Waiting for seller to accept.'
elif bid.state == BidStates.BID_RECEIVED:
state_description = 'Waiting for seller to accept.'
elif bid.state == BidStates.BID_ACCEPTED:
if not bid.initiate_tx:
state_description = 'Waiting for seller to send initiate tx.'
else:
state_description = 'Waiting for initiate tx to confirm.'
elif bid.state == BidStates.SWAP_INITIATED:
state_description = 'Waiting for participate txn to be confirmed in {} chain'.format(ticker_to)
elif bid.state == BidStates.SWAP_PARTICIPATING:
state_description = 'Waiting for initiate txn to be spent in {} chain'.format(ticker_from)
elif bid.state == BidStates.SWAP_COMPLETED:
state_description = 'Swap completed'
if bid.getITxState() == TxStates.TX_REDEEMED and bid.getPTxState() == TxStates.TX_REDEEMED:
state_description += ' successfully'
else:
state_description += ', ITX ' + strTxState(bid.getITxState()) + ', PTX ' + strTxState(bid.getPTxState())
elif bid.state == BidStates.SWAP_TIMEDOUT:
state_description = 'Timed out waiting for initiate txn'
elif bid.state == BidStates.BID_ABANDONED:
state_description = 'Bid abandoned'
elif bid.state == BidStates.BID_ERROR:
state_description = bid.state_note
else:
state_description = ''
data = {
'amt_from': ci_from.format_amount(bid.amount),
'amt_to': ci_to.format_amount((bid.amount * offer.rate) // ci_from.COIN()),
'ticker_from': ticker_from,
'ticker_to': ticker_to,
'bid_state': strBidState(bid.state),
'state_description': state_description,
'itx_state': strTxState(bid.getITxState()),
'ptx_state': strTxState(bid.getPTxState()),
'offer_id': bid.offer_id.hex(),
'addr_from': bid.bid_addr,
'addr_fund_proof': bid.proof_address,
'created_at': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(bid.created_at)),
'expired_at': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(bid.expire_at)),
'was_sent': 'True' if bid.was_sent else 'False',
'was_received': 'True' if bid.was_received else 'False',
'initiate_tx': getTxIdHex(bid, TxTypes.ITX, ' ' + ticker_from),
'initiate_conf': 'None' if (not bid.initiate_tx or not bid.initiate_tx.conf) else bid.initiate_tx.conf,
'participate_tx': getTxIdHex(bid, TxTypes.PTX, ' ' + ticker_to),
'participate_conf': 'None' if (not bid.participate_tx or not bid.participate_tx.conf) else bid.participate_tx.conf,
'show_txns': show_txns,
}
if edit_bid:
data['bid_state_ind'] = int(bid.state)
data['bid_states'] = listBidStates()
if show_txns:
data['initiate_tx_refund'] = 'None' if not bid.initiate_txn_refund else bid.initiate_txn_refund.hex()
data['participate_tx_refund'] = 'None' if not bid.participate_txn_refund else bid.participate_txn_refund.hex()
data['initiate_tx_spend'] = getTxSpendHex(bid, TxTypes.ITX)
data['participate_tx_spend'] = getTxSpendHex(bid, TxTypes.PTX)
return data

View File

@ -27,7 +27,6 @@ from basicswap.util import (
SerialiseNum,
DeserialiseNum,
make_int,
format8,
format_amount,
validate_amount,
)
@ -39,9 +38,6 @@ from basicswap.basicswap import (
SEQUENCE_LOCK_TIME,
)
from basicswap.ecc_util import (
i2b)
class Test(unittest.TestCase):
def test_serialise_num(self):
@ -214,6 +210,5 @@ class Test(unittest.TestCase):
assert('12.00000000' == format_amount(amount_to, scale_to))
if __name__ == '__main__':
unittest.main()

View File

@ -23,14 +23,12 @@ from basicswap.basicswap import (
Coins,
SwapTypes,
BidStates,
TxStates,
DebugTypes,
SEQUENCE_LOCK_BLOCKS,
)
from basicswap.util import (
COIN,
toWIF,
dumpje,
make_int,
)
from basicswap.rpc import (
@ -340,7 +338,6 @@ class Test(unittest.TestCase):
logger.propagate = False
logger.handlers = []
logger.setLevel(logging.INFO) # DEBUG shows many messages from requests.post
#logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s %(levelname)s : %(message)s')
stream_stdout = logging.StreamHandler()
stream_stdout.setFormatter(formatter)
@ -573,15 +570,14 @@ class Test(unittest.TestCase):
end_xmr = float(js_0_end['6']['balance']) + float(js_0_end['6']['unconfirmed'])
assert(end_xmr > 10.9 and end_xmr < 11.0)
self.delay_for(600) # [rm]
def test_02_leader_recover_a_lock_tx(self):
logging.info('---------- Test PART to XMR leader recovers coin a lock tx')
swap_clients = self.swap_clients
js_w0_before = json.loads(urlopen('http://localhost:1800/json/wallets').read())
offer_id = swap_clients[0].postOffer(Coins.PART, Coins.XMR, 101 * COIN, 0.12 * XMR_COIN, 101 * COIN, SwapTypes.XMR_SWAP,
offer_id = swap_clients[0].postOffer(
Coins.PART, Coins.XMR, 101 * COIN, 0.12 * XMR_COIN, 101 * COIN, SwapTypes.XMR_SWAP,
lock_type=SEQUENCE_LOCK_BLOCKS, lock_value=12)
self.wait_for_offer(swap_clients[1], offer_id)
offer = swap_clients[1].getOffer(offer_id)
@ -610,7 +606,8 @@ class Test(unittest.TestCase):
js_w0_before = json.loads(urlopen('http://localhost:1800/json/wallets').read())
offer_id = swap_clients[0].postOffer(Coins.PART, Coins.XMR, 101 * COIN, 0.13 * XMR_COIN, 101 * COIN, SwapTypes.XMR_SWAP,
offer_id = swap_clients[0].postOffer(
Coins.PART, Coins.XMR, 101 * COIN, 0.13 * XMR_COIN, 101 * COIN, SwapTypes.XMR_SWAP,
lock_type=SEQUENCE_LOCK_BLOCKS, lock_value=12)
self.wait_for_offer(swap_clients[1], offer_id)
offer = swap_clients[1].getOffer(offer_id)
@ -637,7 +634,8 @@ class Test(unittest.TestCase):
swap_clients = self.swap_clients
offer_id = swap_clients[0].postOffer(Coins.PART, Coins.XMR, 101 * COIN, 0.14 * XMR_COIN, 101 * COIN, SwapTypes.XMR_SWAP,
offer_id = swap_clients[0].postOffer(
Coins.PART, Coins.XMR, 101 * COIN, 0.14 * XMR_COIN, 101 * COIN, SwapTypes.XMR_SWAP,
lock_type=SEQUENCE_LOCK_BLOCKS, lock_value=18)
self.wait_for_offer(swap_clients[1], offer_id)
offer = swap_clients[1].getOffer(offer_id)
@ -676,8 +674,32 @@ class Test(unittest.TestCase):
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)
def pass_06_delay(self):
self.delay_for(60)
def test_06_multiple_swaps(self):
logging.info('---------- Test Multiple concurrent swaps')
swap_clients = self.swap_clients
offer1_id = swap_clients[0].postOffer(Coins.BTC, Coins.XMR, 10 * COIN, 100 * XMR_COIN, 10 * COIN, SwapTypes.XMR_SWAP)
offer2_id = swap_clients[0].postOffer(Coins.PART, Coins.XMR, 10 * COIN, 0.14 * XMR_COIN, 10 * COIN, SwapTypes.XMR_SWAP)
self.wait_for_offer(swap_clients[1], offer1_id)
offer1 = swap_clients[1].getOffer(offer1_id)
self.wait_for_offer(swap_clients[1], offer2_id)
offer2 = swap_clients[1].getOffer(offer2_id)
bid1_id = swap_clients[1].postXmrBid(offer1_id, offer1.amount_from)
bid2_id = swap_clients[1].postXmrBid(offer2_id, offer2.amount_from)
self.wait_for_bid(swap_clients[0], bid1_id, BidStates.BID_RECEIVED)
swap_clients[0].acceptXmrBid(bid1_id)
self.wait_for_bid(swap_clients[0], bid2_id, BidStates.BID_RECEIVED)
swap_clients[0].acceptXmrBid(bid2_id)
self.wait_for_bid(swap_clients[0], bid1_id, BidStates.SWAP_COMPLETED, wait_for=180)
self.wait_for_bid(swap_clients[1], bid1_id, BidStates.SWAP_COMPLETED, sent=True)
self.wait_for_bid(swap_clients[0], bid2_id, BidStates.SWAP_COMPLETED, wait_for=180)
self.wait_for_bid(swap_clients[1], bid2_id, BidStates.SWAP_COMPLETED, sent=True)
if __name__ == '__main__':

View File

@ -1,2 +1,2 @@
eventtypes
wit