ui: Expose offer valid time.

2024-05-20_merge
tecnovert 4 years ago
parent f2018184e7
commit 36a40b5fa3
No known key found for this signature in database
GPG Key ID: 8ED6D8750C4E3F93
  1. 46
      basicswap/basicswap.py
  2. 3
      basicswap/chainparams.py
  3. 22
      basicswap/ecc_util.py
  4. 29
      basicswap/http_server.py
  5. 18
      basicswap/interface_btc.py
  6. 28
      basicswap/interface_part.py
  7. 6
      basicswap/interface_xmr.py
  8. 3
      basicswap/templates/offer_confirm.html
  9. 3
      basicswap/templates/offer_new_2.html
  10. 19
      basicswap/util.py
  11. 1
      tests/basicswap/common.py
  12. 17
      tests/basicswap/test_xmr.py

@ -870,7 +870,7 @@ class BasicSwap(BaseApp):
ci.initialiseWallet(key_view, key_spend) ci.initialiseWallet(key_view, key_spend)
root_address = ci.getAddressFromKeys(key_view, key_spend) root_address = ci.getAddressFromKeys(key_view, key_spend)
key_str = 'main_wallet_addr_' + chainparams[coin_type]['name'] key_str = 'main_wallet_addr_' + ci.coin_name()
self.setStringKV(key_str, root_address) self.setStringKV(key_str, root_address)
return return
@ -878,7 +878,7 @@ class BasicSwap(BaseApp):
root_hash = ci.getAddressHashFromKey(root_key)[::-1] root_hash = ci.getAddressHashFromKey(root_key)[::-1]
ci.initialiseWallet(root_key) ci.initialiseWallet(root_key)
key_str = 'main_wallet_seedid_' + chainparams[coin_type]['name'] key_str = 'main_wallet_seedid_' + ci.coin_name()
self.setStringKV(key_str, root_hash.hex()) self.setStringKV(key_str, root_hash.hex())
def setIntKVInSession(self, str_key, int_val, session): def setIntKVInSession(self, str_key, int_val, session):
@ -1101,6 +1101,12 @@ class BasicSwap(BaseApp):
else: else:
raise ValueError('Unknown locktype') raise ValueError('Unknown locktype')
def validateOfferValidTime(self, coin_from, coin_to, valid_for_seconds):
if valid_for_seconds < 60 * 60: # SMSG_MIN_TTL
raise ValueError('Offer TTL too low')
if valid_for_seconds > 48 * 60 * 60:
raise ValueError('Offer TTL too high')
def postOffer(self, coin_from, coin_to, amount, rate, min_bid_amount, swap_type, def postOffer(self, coin_from, coin_to, amount, rate, min_bid_amount, swap_type,
lock_type=SEQUENCE_LOCK_TIME, lock_value=48 * 60 * 60, auto_accept_bids=False, addr_send_from=None, extra_options={}): lock_type=SEQUENCE_LOCK_TIME, lock_value=48 * 60 * 60, auto_accept_bids=False, addr_send_from=None, extra_options={}):
# Offer to send offer.amount_from of coin_from in exchange for offer.amount_from * offer.rate of coin_to # Offer to send offer.amount_from of coin_from in exchange for offer.amount_from * offer.rate of coin_to
@ -1117,9 +1123,12 @@ class BasicSwap(BaseApp):
except Exception: except Exception:
raise ValueError('Unknown coin to type') raise ValueError('Unknown coin to type')
valid_for_seconds = extra_options.get('valid_for_seconds', 60 * 60)
self.validateSwapType(coin_from_t, coin_to_t, swap_type) self.validateSwapType(coin_from_t, coin_to_t, swap_type)
self.validateOfferAmounts(coin_from_t, coin_to_t, amount, rate, min_bid_amount) self.validateOfferAmounts(coin_from_t, coin_to_t, amount, rate, min_bid_amount)
self.validateOfferLockValue(coin_from_t, coin_to_t, lock_type, lock_value) self.validateOfferLockValue(coin_from_t, coin_to_t, lock_type, lock_value)
self.validateOfferValidTime(coin_from_t, coin_to_t, valid_for_seconds)
self.mxDB.acquire() self.mxDB.acquire()
session = None session = None
@ -1136,7 +1145,7 @@ class BasicSwap(BaseApp):
msg_buf.rate = int(rate) msg_buf.rate = int(rate)
msg_buf.min_bid_amount = int(min_bid_amount) msg_buf.min_bid_amount = int(min_bid_amount)
msg_buf.time_valid = 60 * 60 msg_buf.time_valid = valid_for_seconds
msg_buf.lock_type = lock_type msg_buf.lock_type = lock_type
msg_buf.lock_value = lock_value msg_buf.lock_value = lock_value
msg_buf.swap_type = swap_type msg_buf.swap_type = swap_type
@ -1424,7 +1433,9 @@ class BasicSwap(BaseApp):
self.log.info('withdrawCoin %s %s to %s %s', value, self.getTicker(coin_type), addr_to, ' subfee' if subfee else '') self.log.info('withdrawCoin %s %s to %s %s', value, self.getTicker(coin_type), addr_to, ' subfee' if subfee else '')
ci = self.ci(coin_type) ci = self.ci(coin_type)
return ci.withdrawCoin(value, addr_to, subfee) txid = ci.withdrawCoin(value, addr_to, subfee)
self.log.debug('In txn: {}'.format(txid))
return txid
def withdrawParticl(self, type_from, type_to, value, addr_to, subfee): def withdrawParticl(self, type_from, type_to, value, addr_to, subfee):
self.log.info('withdrawParticl %s %s to %s %s %s', value, type_from, type_to, addr_to, ' subfee' if subfee else '') self.log.info('withdrawParticl %s %s to %s %s %s', value, type_from, type_to, addr_to, ' subfee' if subfee else '')
@ -1435,7 +1446,9 @@ class BasicSwap(BaseApp):
type_to = 'part' type_to = 'part'
ci = self.ci(Coins.PART) ci = self.ci(Coins.PART)
return ci.sendTypeTo(type_from, type_to, value, addr_to, subfee) txid = ci.sendTypeTo(type_from, type_to, value, addr_to, subfee)
self.log.debug('In txn: {}'.format(txid))
return txid
def cacheNewAddressForCoin(self, coin_type): def cacheNewAddressForCoin(self, coin_type):
self.log.debug('cacheNewAddressForCoin %s', coin_type) self.log.debug('cacheNewAddressForCoin %s', coin_type)
@ -1449,7 +1462,7 @@ class BasicSwap(BaseApp):
if c == Coins.PART: if c == Coins.PART:
return True # TODO return True # TODO
if c == Coins.XMR: if c == Coins.XMR:
expect_address = self.getStringKV('main_wallet_addr_' + chainparams[c]['name']) expect_address = self.getStringKV('main_wallet_addr_' + ci.coin_name())
if expect_address is None: if expect_address is None:
self.log.warning('Can\'t find expected main wallet address for coin {}'.format(ci.coin_name())) self.log.warning('Can\'t find expected main wallet address for coin {}'.format(ci.coin_name()))
return False return False
@ -1459,7 +1472,7 @@ class BasicSwap(BaseApp):
self.log.warning('Wallet for coin {} not derived from swap seed.'.format(ci.coin_name())) self.log.warning('Wallet for coin {} not derived from swap seed.'.format(ci.coin_name()))
return False return False
expect_seedid = self.getStringKV('main_wallet_seedid_' + chainparams[c]['name']) expect_seedid = self.getStringKV('main_wallet_seedid_' + ci.coin_name())
if expect_seedid is None: if expect_seedid is None:
self.log.warning('Can\'t find expected wallet seed id for coin {}'.format(ci.coin_name())) self.log.warning('Can\'t find expected wallet seed id for coin {}'.format(ci.coin_name()))
return False return False
@ -1976,7 +1989,7 @@ class BasicSwap(BaseApp):
bid.initiate_txn_refund = bytes.fromhex(refund_txn) bid.initiate_txn_refund = bytes.fromhex(refund_txn)
txid = self.submitTxn(coin_from, txn) txid = self.submitTxn(coin_from, txn)
self.log.debug('Submitted initiate txn %s to %s chain for bid %s', txid, chainparams[coin_from]['name'], bid_id.hex()) self.log.debug('Submitted initiate txn %s to %s chain for bid %s', txid, ci_from.coin_name(), bid_id.hex())
bid.initiate_tx = SwapTx( bid.initiate_tx = SwapTx(
bid_id=bid_id, bid_id=bid_id,
tx_type=TxTypes.ITX, tx_type=TxTypes.ITX,
@ -2061,7 +2074,6 @@ class BasicSwap(BaseApp):
xmr_swap.b_restore_height = wallet_restore_height xmr_swap.b_restore_height = wallet_restore_height
self.log.warning('XMR swap restore height clamped to {}'.format(wallet_restore_height)) self.log.warning('XMR swap restore height clamped to {}'.format(wallet_restore_height))
for_ed25519 = True if coin_to == Coins.XMR else False 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) 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) kbsf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 2, for_ed25519)
@ -3617,6 +3629,7 @@ class BasicSwap(BaseApp):
self.validateSwapType(coin_from, coin_to, offer_data.swap_type) self.validateSwapType(coin_from, coin_to, offer_data.swap_type)
self.validateOfferAmounts(coin_from, coin_to, offer_data.amount_from, offer_data.rate, offer_data.min_bid_amount) self.validateOfferAmounts(coin_from, coin_to, offer_data.amount_from, offer_data.rate, offer_data.min_bid_amount)
self.validateOfferLockValue(coin_from, coin_to, offer_data.lock_type, offer_data.lock_value) self.validateOfferLockValue(coin_from, coin_to, offer_data.lock_type, offer_data.lock_value)
self.validateOfferValidTime(coin_from, coin_to, offer_data.time_valid)
assert(offer_data.time_valid >= MIN_OFFER_VALID_TIME and offer_data.time_valid <= MAX_OFFER_VALID_TIME), 'Invalid time_valid' assert(offer_data.time_valid >= MIN_OFFER_VALID_TIME and offer_data.time_valid <= MAX_OFFER_VALID_TIME), 'Invalid time_valid'
assert(msg['sent'] + offer_data.time_valid >= now), 'Offer expired' assert(msg['sent'] + offer_data.time_valid >= now), 'Offer expired'
@ -3629,9 +3642,8 @@ class BasicSwap(BaseApp):
elif offer_data.swap_type == SwapTypes.BUYER_FIRST: elif offer_data.swap_type == SwapTypes.BUYER_FIRST:
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 not in (Coins.XMR, Coins.PART_ANON))
assert(coin_from != Coins.PART_ANON) assert(coin_to in (Coins.XMR, 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))
@ -4225,7 +4237,7 @@ class BasicSwap(BaseApp):
a_lock_tx_id = ci_from.getTxHash(xmr_swap.a_lock_tx) a_lock_tx_id = ci_from.getTxHash(xmr_swap.a_lock_tx)
a_lock_tx_vout = ci_from.getTxOutputPos(xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script) a_lock_tx_vout = ci_from.getTxOutputPos(xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script)
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()) self.log.debug('Waiting for lock txn %s to %s chain for bid %s', a_lock_tx_id.hex(), ci_from.coin_name(), bid_id.hex())
bid.xmr_a_lock_tx = SwapTx( bid.xmr_a_lock_tx = SwapTx(
bid_id=bid_id, bid_id=bid_id,
tx_type=TxTypes.XMR_SWAP_A_LOCK, tx_type=TxTypes.XMR_SWAP_A_LOCK,
@ -4292,7 +4304,7 @@ class BasicSwap(BaseApp):
vout_pos = ci_from.getTxOutputPos(xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script) vout_pos = ci_from.getTxOutputPos(xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script)
self.log.debug('Submitted lock txn %s to %s chain for bid %s', txid_hex, chainparams[coin_from]['name'], bid_id.hex()) self.log.debug('Submitted lock txn %s to %s chain for bid %s', txid_hex, ci_from.coin_name(), bid_id.hex())
bid.xmr_a_lock_tx = SwapTx( bid.xmr_a_lock_tx = SwapTx(
bid_id=bid_id, bid_id=bid_id,
@ -4356,7 +4368,7 @@ class BasicSwap(BaseApp):
self.logBidEvent(bid, EventLogTypes.FAILED_TX_B_LOCK_PUBLISH, str_error, session) self.logBidEvent(bid, EventLogTypes.FAILED_TX_B_LOCK_PUBLISH, str_error, session)
return 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()) self.log.debug('Submitted lock txn %s to %s chain for bid %s', b_lock_tx_id.hex(), ci_to.coin_name(), bid_id.hex())
bid.xmr_b_lock_tx = SwapTx( bid.xmr_b_lock_tx = SwapTx(
bid_id=bid_id, bid_id=bid_id,
tx_type=TxTypes.XMR_SWAP_B_LOCK, tx_type=TxTypes.XMR_SWAP_B_LOCK,
@ -4435,7 +4447,7 @@ class BasicSwap(BaseApp):
xmr_swap.a_lock_spend_tx = ci_from.setTxSignature(xmr_swap.a_lock_spend_tx, witness_stack) 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)) 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()) self.log.debug('Submitted lock spend txn %s to %s chain for bid %s', txid.hex(), ci_from.coin_name(), bid_id.hex())
bid.xmr_a_lock_spend_tx = SwapTx( bid.xmr_a_lock_spend_tx = SwapTx(
bid_id=bid_id, bid_id=bid_id,
tx_type=TxTypes.XMR_SWAP_A_LOCK_SPEND, tx_type=TxTypes.XMR_SWAP_A_LOCK_SPEND,
@ -4980,7 +4992,7 @@ class BasicSwap(BaseApp):
rv = { rv = {
'version': self.coin_clients[coin]['core_version'], 'version': self.coin_clients[coin]['core_version'],
'deposit_address': self.getCachedAddressForCoin(coin), 'deposit_address': self.getCachedAddressForCoin(coin),
'name': chainparams[coin]['name'].capitalize(), 'name': ci.coin_name().capitalize(),
'blocks': blockchaininfo['blocks'], 'blocks': blockchaininfo['blocks'],
'balance': format_amount(make_int(walletinfo['balance'], scale), scale), 'balance': format_amount(make_int(walletinfo['balance'], scale), scale),
'unconfirmed': format_amount(make_int(walletinfo.get('unconfirmed_balance'), scale), scale), 'unconfirmed': format_amount(make_int(walletinfo.get('unconfirmed_balance'), scale), scale),

@ -37,6 +37,7 @@ chainparams = {
'pubkey_address': 0x38, 'pubkey_address': 0x38,
'script_address': 0x3c, 'script_address': 0x3c,
'key_prefix': 0x6c, 'key_prefix': 0x6c,
'stealth_key_prefix': 0x14,
'hrp': 'pw', 'hrp': 'pw',
'bip44': 44, 'bip44': 44,
'min_amount': 1000, 'min_amount': 1000,
@ -47,6 +48,7 @@ chainparams = {
'pubkey_address': 0x76, 'pubkey_address': 0x76,
'script_address': 0x7a, 'script_address': 0x7a,
'key_prefix': 0x2e, 'key_prefix': 0x2e,
'stealth_key_prefix': 0x15,
'hrp': 'tpw', 'hrp': 'tpw',
'bip44': 1, 'bip44': 1,
'min_amount': 1000, 'min_amount': 1000,
@ -57,6 +59,7 @@ chainparams = {
'pubkey_address': 0x76, 'pubkey_address': 0x76,
'script_address': 0x7a, 'script_address': 0x7a,
'key_prefix': 0x2e, 'key_prefix': 0x2e,
'stealth_key_prefix': 0x15,
'hrp': 'rtpw', 'hrp': 'rtpw',
'bip44': 1, 'bip44': 1,
'min_amount': 1000, 'min_amount': 1000,

@ -33,42 +33,42 @@ G = Point(curve_secp256k1, ep.Gx, ep.Gy, ep.o)
SECP256K1_ORDER_HALF = ep.o // 2 SECP256K1_ORDER_HALF = ep.o // 2
def ToDER(P): def ToDER(P) -> bytes:
return bytes((4, )) + int(P.x()).to_bytes(32, byteorder='big') + int(P.y()).to_bytes(32, byteorder='big') return bytes((4, )) + int(P.x()).to_bytes(32, byteorder='big') + int(P.y()).to_bytes(32, byteorder='big')
def bytes32ToInt(b): def bytes32ToInt(b) -> int:
return int.from_bytes(b, byteorder='big') return int.from_bytes(b, byteorder='big')
def intToBytes32(i): def intToBytes32(i: int) -> bytes:
return i.to_bytes(32, byteorder='big') return i.to_bytes(32, byteorder='big')
def intToBytes32_le(i): def intToBytes32_le(i: int) -> bytes:
return i.to_bytes(32, byteorder='little') return i.to_bytes(32, byteorder='little')
def bytesToHexStr(b): def bytesToHexStr(b: bytes) -> str:
return codecs.encode(b, 'hex').decode('utf-8') return codecs.encode(b, 'hex').decode('utf-8')
def hexStrToBytes(h): def hexStrToBytes(h: str) -> bytes:
if h.startswith('0x'): if h.startswith('0x'):
h = h[2:] h = h[2:]
return bytes.fromhex(h) return bytes.fromhex(h)
def getSecretBytes(): def getSecretBytes() -> bytes:
i = 1 + secrets.randbelow(ep.o - 1) i = 1 + secrets.randbelow(ep.o - 1)
return intToBytes32(i) return intToBytes32(i)
def getSecretInt(): def getSecretInt() -> int:
return 1 + secrets.randbelow(ep.o - 1) return 1 + secrets.randbelow(ep.o - 1)
def getInsecureBytes(): def getInsecureBytes() -> bytes:
while True: while True:
s = os.urandom(32) s = os.urandom(32)
@ -77,7 +77,7 @@ def getInsecureBytes():
return s return s
def getInsecureInt(): def getInsecureInt() -> int:
while True: while True:
s = os.urandom(32) s = os.urandom(32)
@ -86,7 +86,7 @@ def getInsecureInt():
return s_test return s_test
def powMod(x, y, z): def powMod(x, y, z) -> int:
# Calculate (x ** y) % z efficiently. # Calculate (x ** y) % z efficiently.
number = 1 number = 1
while y: while y:

@ -66,11 +66,14 @@ def getCoinName(c):
def listAvailableCoins(swap_client): def listAvailableCoins(swap_client):
coins = [] coins = []
for k, v in swap_client.coin_clients.items(): for k, v in swap_client.coin_clients.items():
if k not in chainparams:
continue
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: if k == Coins.PART:
coins.append((int(Coins.PART_ANON), getCoinName(k))) pass
# TODO: Uncomment
# coins.append((int(Coins.PART_ANON), getCoinName(k)))
return coins return coins
@ -402,14 +405,17 @@ class HttpHandler(BaseHTTPRequestHandler):
page_data['coin_to'] = getCoinType(get_data_entry(form_data, 'coin_to')) page_data['coin_to'] = getCoinType(get_data_entry(form_data, 'coin_to'))
coin_to = Coins(page_data['coin_to']) coin_to = Coins(page_data['coin_to'])
ci_to = swap_client.ci(coin_to) ci_to = swap_client.ci(coin_to)
if coin_to != Coins.XMR:
page_data['fee_to_conf'] = ci_to._conf_target # Set default value
parsed_data['coin_to'] = coin_to parsed_data['coin_to'] = coin_to
if coin_to == Coins.XMR:
page_data['swap_style'] = 'xmr'
else:
page_data['swap_style'] = 'atomic'
except Exception: except Exception:
errors.append('Unknown Coin To') errors.append('Unknown Coin To')
if parsed_data['coin_to'] in (Coins.XMR, Coins.PART_ANON):
page_data['swap_style'] = 'xmr'
else:
page_data['swap_style'] = 'atomic'
try: try:
page_data['amt_from'] = get_data_entry(form_data, 'amt_from') page_data['amt_from'] = get_data_entry(form_data, 'amt_from')
parsed_data['amt_from'] = inputAmount(page_data['amt_from'], ci_from) parsed_data['amt_from'] = inputAmount(page_data['amt_from'], ci_from)
@ -460,6 +466,12 @@ class HttpHandler(BaseHTTPRequestHandler):
elif have_data_entry(form_data, 'lockseconds'): elif have_data_entry(form_data, 'lockseconds'):
parsed_data['lock_seconds'] = int(get_data_entry(form_data, 'lockseconds')) parsed_data['lock_seconds'] = int(get_data_entry(form_data, 'lockseconds'))
if have_data_entry(form_data, 'validhrs'):
page_data['validhrs'] = int(get_data_entry(form_data, 'validhrs'))
parsed_data['valid_for_seconds'] = page_data['validhrs'] * 60 * 60
elif have_data_entry(form_data, 'valid_for_seconds'):
parsed_data['valid_for_seconds'] = int(get_data_entry(form_data, 'valid_for_seconds'))
page_data['autoaccept'] = True if have_data_entry(form_data, 'autoaccept') else False page_data['autoaccept'] = True if have_data_entry(form_data, 'autoaccept') else False
parsed_data['autoaccept'] = page_data['autoaccept'] parsed_data['autoaccept'] = page_data['autoaccept']
@ -499,7 +511,7 @@ class HttpHandler(BaseHTTPRequestHandler):
swap_client = self.server.swap_client swap_client = self.server.swap_client
swap_type = SwapTypes.SELLER_FIRST swap_type = SwapTypes.SELLER_FIRST
if parsed_data['coin_to'] == Coins.XMR: if parsed_data['coin_to'] in (Coins.XMR, Coins.PART_ANON):
swap_type = SwapTypes.XMR_SWAP swap_type = SwapTypes.XMR_SWAP
if swap_client.coin_clients[parsed_data['coin_from']]['use_csv'] and swap_client.coin_clients[parsed_data['coin_to']]['use_csv']: if swap_client.coin_clients[parsed_data['coin_from']]['use_csv'] and swap_client.coin_clients[parsed_data['coin_to']]['use_csv']:
@ -522,6 +534,8 @@ class HttpHandler(BaseHTTPRequestHandler):
extra_options['to_fee_multiplier_percent'] = parsed_data['fee_to_extra'] extra_options['to_fee_multiplier_percent'] = parsed_data['fee_to_extra']
if 'to_fee_override' in parsed_data: if 'to_fee_override' in parsed_data:
extra_options['to_fee_override'] = parsed_data['to_fee_override'] extra_options['to_fee_override'] = parsed_data['to_fee_override']
if 'valid_for_seconds' in parsed_data:
extra_options['valid_for_seconds'] = parsed_data['valid_for_seconds']
offer_id = swap_client.postOffer( offer_id = swap_client.postOffer(
parsed_data['coin_from'], parsed_data['coin_from'],
@ -552,6 +566,7 @@ class HttpHandler(BaseHTTPRequestHandler):
# Set defaults # Set defaults
'fee_from_conf': 2, 'fee_from_conf': 2,
'fee_to_conf': 2, 'fee_to_conf': 2,
'validhrs': 1,
'lockhrs': 32, 'lockhrs': 32,
'autoaccept': True 'autoaccept': True
} }

@ -34,7 +34,7 @@ from coincurve.ecdsaotves import (
ecdsaotves_rec_enc_key) ecdsaotves_rec_enc_key)
from .ecc_util import ( from .ecc_util import (
G, ep, ep,
pointToCPK, CPKToPoint, pointToCPK, CPKToPoint,
getSecretInt, getSecretInt,
b2h, i2b, b2i, i2h) b2h, i2b, b2i, i2h)
@ -92,23 +92,23 @@ class BTCInterface(CoinInterface):
return COIN return COIN
@staticmethod @staticmethod
def exp(): def exp() -> int:
return 8 return 8
@staticmethod @staticmethod
def nbk(): def nbk() -> int:
return 32 return 32
@staticmethod @staticmethod
def nbK(): # No. of bytes requires to encode a public key def nbK() -> int: # No. of bytes requires to encode a public key
return 33 return 33
@staticmethod @staticmethod
def witnessScaleFactor(): def witnessScaleFactor() -> int:
return 4 return 4
@staticmethod @staticmethod
def txVersion(): def txVersion() -> int:
return 2 return 2
@staticmethod @staticmethod
@ -119,11 +119,11 @@ class BTCInterface(CoinInterface):
return rv return rv
@staticmethod @staticmethod
def compareFeeRates(a, b): def compareFeeRates(a, b) -> bool:
return abs(a - b) < 20 return abs(a - b) < 20
@staticmethod @staticmethod
def xmr_swap_alock_spend_tx_vsize(): def xmr_swap_alock_spend_tx_vsize() -> int:
return 147 return 147
@staticmethod @staticmethod
@ -921,7 +921,7 @@ class BTCInterface(CoinInterface):
rv = pubkey.verify_compact(sig, message_hash, hasher=None) rv = pubkey.verify_compact(sig, message_hash, hasher=None)
assert(rv is True) assert(rv is True)
def verifyMessage(self, address, message, signature, message_magic=None): def verifyMessage(self, address, message, signature, message_magic=None) -> bool:
if message_magic is None: if message_magic is None:
message_magic = chainparams[self.coin_type()]['message_magic'] message_magic = chainparams[self.coin_type()]['message_magic']

@ -15,8 +15,9 @@ from .contrib.test_framework.script import (
OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG
) )
from .util import encodeStealthAddress
from .chainparams import Coins, chainparams
from .interface_btc import BTCInterface from .interface_btc import BTCInterface
from .chainparams import Coins
class BalanceTypes(IntEnum): class BalanceTypes(IntEnum):
@ -35,22 +36,22 @@ class PARTInterface(BTCInterface):
return BalanceTypes.PLAIN return BalanceTypes.PLAIN
@staticmethod @staticmethod
def witnessScaleFactor(): def witnessScaleFactor() -> int:
return 2 return 2
@staticmethod @staticmethod
def txVersion(): def txVersion() -> int:
return 0xa0 return 0xa0
@staticmethod @staticmethod
def xmr_swap_alock_spend_tx_vsize(): def xmr_swap_alock_spend_tx_vsize() -> int:
return 213 return 213
@staticmethod @staticmethod
def txoType(): def txoType():
return CTxOutPart return CTxOutPart
def setDefaults(self): def setDefaults(self) -> None:
super().setDefaults() super().setDefaults()
self._anon_tx_ring_size = 8 # TODO: Make option self._anon_tx_ring_size = 8 # TODO: Make option
@ -86,6 +87,11 @@ class PARTInterface(BTCInterface):
def getScriptForPubkeyHash(self, pkh): def getScriptForPubkeyHash(self, pkh):
return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG]) return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
def formatStealthAddress(self, scan_pubkey, spend_pubkey):
prefix_byte = chainparams[self.coin_type()][self._network]['stealth_key_prefix']
return encodeStealthAddress(prefix_byte, scan_pubkey, spend_pubkey)
class PARTInterfaceBlind(PARTInterface): class PARTInterfaceBlind(PARTInterface):
@staticmethod @staticmethod
@ -99,7 +105,17 @@ class PARTInterfaceAnon(PARTInterface):
return BalanceTypes.ANON return BalanceTypes.ANON
def publishBLockTx(self, Kbv, Kbs, output_amount, feerate): def publishBLockTx(self, Kbv, Kbs, output_amount, feerate):
raise ValueError('TODO - new core release') sx_addr = self.formatStealthAddress(Kbv, Kbs)
self._log.debug('sx_addr: {}'.format(sx_addr))
# TODO: Fund from other balances
params = ['anon', 'anon',
[{'address': sx_addr, 'amount': self.format_amount(output_amount)}, ],
'', '', self._anon_tx_ring_size, 1, False,
{'conf_target': self._conf_target, 'blind_watchonly_visible': True}]
txid = self.rpc_callback('sendtypeto', params)
return bytes.fromhex(txid)
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):
raise ValueError('TODO - new core release') raise ValueError('TODO - new core release')

@ -47,15 +47,15 @@ class XMRInterface(CoinInterface):
return XMR_COIN return XMR_COIN
@staticmethod @staticmethod
def exp(): def exp() -> int:
return 12 return 12
@staticmethod @staticmethod
def nbk(): def nbk() -> int:
return 32 return 32
@staticmethod @staticmethod
def nbK(): # No. of bytes requires to encode a public key def nbK() -> int: # No. of bytes requires to encode a public key
return 32 return 32
def __init__(self, coin_settings, network, swap_client=None): def __init__(self, coin_settings, network, swap_client=None):

@ -54,7 +54,8 @@
</select></td></tr> </select></td></tr>
{% endif %} {% endif %}
<tr class="padded_row"><td>Contract locked (hrs)</td><td><input type="number" name="lockhrs" min="1" max="64" value="{{ data.lockhrs }}" readonly></td>{% if data.swap_style != 'xmr' %}<td colspan=2>Participate txn will be locked for half the time.</td>{% endif %}</tr> <tr class="padded_row"><td>Offer valid (hrs)</td><td><input type="number" name="validhrs" min="1" max="48" value="{{ data.validhrs }}" readonly></td></tr>
<tr><td>Contract locked (hrs)</td><td><input type="number" name="lockhrs" min="1" max="64" value="{{ data.lockhrs }}" readonly></td>{% if data.swap_style != 'xmr' %}<td colspan=2>Participate txn will be locked for half the time.</td>{% endif %}</tr>
<tr><td>Auto Accept Bids</td><td colspan=3><input type="checkbox" name="autoaccept_" value="aa" {% if data.autoaccept==true %} checked="true"{% endif %} disabled></td></tr> <tr><td>Auto Accept Bids</td><td colspan=3><input type="checkbox" name="autoaccept_" value="aa" {% if data.autoaccept==true %} checked="true"{% endif %} disabled></td></tr>
</table> </table>

@ -51,7 +51,8 @@
</select></td></tr> </select></td></tr>
{% endif %} {% endif %}
<tr class="padded_row"><td>Contract locked (hrs)</td><td><input type="number" name="lockhrs" min="1" max="96" value="{{ data.lockhrs }}"></td><td colspan=2>Participate txn will be locked for half the time.</td></tr> <tr class="padded_row"><td>Offer valid (hrs)</td><td><input type="number" name="validhrs" min="1" max="48" value="{{ data.validhrs }}"></td></tr>
<tr><td>Contract locked (hrs)</td><td><input type="number" name="lockhrs" min="1" max="96" value="{{ data.lockhrs }}"></td>{% if data.swap_style != 'xmr' %}<td colspan=2>Participate txn will be locked for half the time.</td>{% endif %}</tr>
<tr><td>Auto Accept Bids</td><td colspan=3><input type="checkbox" name="autoaccept" value="aa" {% if data.autoaccept==true %} checked="true"{% endif %}></td></tr> <tr><td>Auto Accept Bids</td><td colspan=3><input type="checkbox" name="autoaccept" value="aa" {% if data.autoaccept==true %} checked="true"{% endif %}></td></tr>
</table> </table>

@ -27,7 +27,7 @@ def assert_cond(v, err='Bad opcode'):
raise ValueError(err) raise ValueError(err)
def toBool(s): def toBool(s) -> bool:
return s.lower() in ["1", "true"] return s.lower() in ["1", "true"]
@ -170,7 +170,7 @@ def SerialiseNum(n):
return bytes((len(rv),)) + rv return bytes((len(rv),)) + rv
def DeserialiseNum(b, o=0): def DeserialiseNum(b, o=0) -> int:
if b[o] == 0: if b[o] == 0:
return 0 return 0
if b[o] > 0x50 and b[o] <= 0x50 + 16: if b[o] > 0x50 and b[o] <= 0x50 + 16:
@ -276,7 +276,7 @@ def make_int(v, scale=8, r=0): # r = 0, no rounding, fail, r > 0 round up, r <
return rv return rv
def validate_amount(amount, scale=8): def validate_amount(amount, scale=8) -> bool:
str_amount = float_to_str(amount) if type(amount) == float else str(amount) str_amount = float_to_str(amount) if type(amount) == float else str(amount)
has_decimal = False has_decimal = False
for c in str_amount: for c in str_amount:
@ -324,3 +324,16 @@ def getP2SHScriptForHash(p2sh):
def getP2WSH(script): def getP2WSH(script):
return bytes((OpCodes.OP_0, 0x20)) + hashlib.sha256(script).digest() return bytes((OpCodes.OP_0, 0x20)) + hashlib.sha256(script).digest()
def encodeStealthAddress(prefix_byte, scan_pubkey, spend_pubkey):
data = bytes((0x00,))
data += scan_pubkey
data += bytes((0x01,))
data += spend_pubkey
data += bytes((0x00,)) # number_signatures - unused
data += bytes((0x00,)) # num prefix bits
b = bytes((prefix_byte,)) + data
b += hashlib.sha256(hashlib.sha256(b).digest()).digest()[:4]
return b58encode(b)

@ -235,6 +235,7 @@ def wait_for_balance(delay_event, url, balance_key, expect_amount, iterations=20
i = 0 i = 0
while not delay_event.is_set(): while not delay_event.is_set():
rv_js = json.loads(urlopen(url).read()) rv_js = json.loads(urlopen(url).read())
print("[rm] rv_js", rv_js)
if float(rv_js[balance_key]) >= expect_amount: if float(rv_js[balance_key]) >= expect_amount:
break break
delay_event.wait(delay_time) delay_event.wait(delay_time)

@ -703,9 +703,10 @@ class Test(unittest.TestCase):
js_1 = json.loads(urlopen('http://127.0.0.1:1801/json/wallets/part').read()) js_1 = json.loads(urlopen('http://127.0.0.1:1801/json/wallets/part').read())
assert(float(js_1['balance']) > 200.0) assert(float(js_1['balance']) > 200.0)
callnoderpc(1, 'reservebalance', [True, 1000000]) # Stop staking to avoid conflicts (input used by tx->anon staked before tx gets in the chain)
post_json = { post_json = {
'value': 100, 'value': 100,
'address': js_0['stealth_address'], 'address': js_1['stealth_address'],
'subfee': False, 'subfee': False,
'type_to': 'anon', 'type_to': 'anon',
} }
@ -718,8 +719,13 @@ class Test(unittest.TestCase):
assert(len(json_rv['txid']) == 64) assert(len(json_rv['txid']) == 64)
logging.info('Waiting for anon balance') logging.info('Waiting for anon balance')
wait_for_balance(test_delay_event, 'http://127.0.0.1:1800/json/wallets/part', 'anon_balance', 110.0) try:
wait_for_balance(test_delay_event, 'http://127.0.0.1:1801/json/wallets/part', 'anon_balance', 110.0)
except Exception as e:
ft = callnoderpc(0, 'filtertransactions', [{'count': 0}])
raise e
callnoderpc(1, 'reservebalance', [False])
post_json = { post_json = {
'value': 10, 'value': 10,
'address': js_0['stealth_address'], 'address': js_0['stealth_address'],
@ -727,7 +733,7 @@ class Test(unittest.TestCase):
'type_from': 'anon', 'type_from': 'anon',
'type_to': 'blind', 'type_to': 'blind',
} }
json_rv = json.loads(post_json_req('http://127.0.0.1:1800/json/wallets/part/withdraw', post_json)) json_rv = json.loads(post_json_req('http://127.0.0.1:1801/json/wallets/part/withdraw', post_json))
assert(len(json_rv['txid']) == 64) assert(len(json_rv['txid']) == 64)
logging.info('Waiting for blind balance') logging.info('Waiting for blind balance')
@ -735,14 +741,13 @@ 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 # TODO
return
amt_swap = make_int(random.uniform(0.1, 2.0), scale=8, r=1) 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) 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) 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) wait_for_offer(test_delay_event, swap_clients[1], offer_id)
offers = swap_clients[1].listOffers(filters={'offer_id': offer_id}) offers = swap_clients[0].listOffers(filters={'offer_id': offer_id})
offer = offers[0] offer = offers[0]
bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from) bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from)

Loading…
Cancel
Save