Raise version, transmit amount to instead of rate.

2024-05-20_merge
tecnovert 8 months ago
parent 463c51c822
commit 8a279dc71f
No known key found for this signature in database
GPG Key ID: 8ED6D8750C4E3F93
  1. 2
      basicswap/__init__.py
  2. 131
      basicswap/basicswap.py
  3. 3
      basicswap/db.py
  4. 6
      basicswap/db_upgrades.py
  5. 11
      basicswap/js_server.py
  6. 121
      basicswap/messages.proto
  7. 61
      basicswap/messages_pb2.py
  8. 11
      basicswap/ui/page_offers.py
  9. 4
      basicswap/ui/util.py
  10. 10
      doc/release-notes.md
  11. 2
      tests/basicswap/test_btc_xmr.py
  12. 94
      tests/basicswap/test_other.py

@ -1,3 +1,3 @@
name = "basicswap" name = "basicswap"
__version__ = "0.12.7" __version__ = "0.13.0"

@ -147,11 +147,11 @@ from basicswap.db_util import (
remove_expired_data, remove_expired_data,
) )
PROTOCOL_VERSION_SECRET_HASH = 3 PROTOCOL_VERSION_SECRET_HASH = 4
MINPROTO_VERSION_SECRET_HASH = 2 MINPROTO_VERSION_SECRET_HASH = 4
PROTOCOL_VERSION_ADAPTOR_SIG = 3 PROTOCOL_VERSION_ADAPTOR_SIG = 4
MINPROTO_VERSION_ADAPTOR_SIG = 3 MINPROTO_VERSION_ADAPTOR_SIG = 4
def validOfferStateToReceiveBid(offer_state): def validOfferStateToReceiveBid(offer_state):
@ -1455,14 +1455,13 @@ class BasicSwap(BaseApp):
finally: finally:
self.closeSession(session) self.closeSession(session)
def validateOfferAmounts(self, coin_from, coin_to, amount: int, rate: int, min_bid_amount: int) -> None: def validateOfferAmounts(self, coin_from, coin_to, amount: int, amount_to: int, min_bid_amount: int) -> None:
ci_from = self.ci(coin_from) ci_from = self.ci(coin_from)
ci_to = self.ci(coin_to) ci_to = self.ci(coin_to)
ensure(amount >= min_bid_amount, 'amount < min_bid_amount') ensure(amount >= min_bid_amount, 'amount < min_bid_amount')
ensure(amount > ci_from.min_amount(), 'From amount below min value for chain') ensure(amount > ci_from.min_amount(), 'From amount below min value for chain')
ensure(amount < ci_from.max_amount(), 'From amount above max value for chain') ensure(amount < ci_from.max_amount(), 'From amount above max value for chain')
amount_to = int((amount * rate) // ci_from.COIN())
ensure(amount_to > ci_to.min_amount(), 'To amount below min value for chain') ensure(amount_to > ci_to.min_amount(), 'To amount below min value for chain')
ensure(amount_to < ci_to.max_amount(), 'To amount above max value for chain') ensure(amount_to < ci_to.max_amount(), 'To amount above max value for chain')
@ -1524,7 +1523,7 @@ class BasicSwap(BaseApp):
return extra_options['addr_send_to'] return extra_options['addr_send_to']
return self.network_addr return self.network_addr
def postOffer(self, coin_from, coin_to, amount: int, rate, min_bid_amount: int, swap_type, def postOffer(self, coin_from, coin_to, amount: int, rate: int, min_bid_amount: int, swap_type,
lock_type=TxLockTypes.SEQUENCE_LOCK_TIME, lock_value: int = 48 * 60 * 60, auto_accept_bids: bool = False, addr_send_from: str = None, extra_options={}) -> bytes: lock_type=TxLockTypes.SEQUENCE_LOCK_TIME, lock_value: int = 48 * 60 * 60, auto_accept_bids: bool = False, addr_send_from: str = None, extra_options={}) -> bytes:
# 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
@ -1540,10 +1539,14 @@ 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) valid_for_seconds: int = extra_options.get('valid_for_seconds', 60 * 60)
amount_to: int = extra_options.get('amount_to', int((amount * rate) // ci_from.COIN()))
# Recalculate the rate so it will match the bid rate
rate = ci_from.make_int(amount_to / amount, r=1)
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, amount_to, min_bid_amount)
self.validateOfferLockValue(swap_type, coin_from_t, coin_to_t, lock_type, lock_value) self.validateOfferLockValue(swap_type, coin_from_t, coin_to_t, lock_type, lock_value)
self.validateOfferValidTime(swap_type, coin_from_t, coin_to_t, valid_for_seconds) self.validateOfferValidTime(swap_type, coin_from_t, coin_to_t, valid_for_seconds)
@ -1564,7 +1567,7 @@ class BasicSwap(BaseApp):
msg_buf.coin_from = int(coin_from) msg_buf.coin_from = int(coin_from)
msg_buf.coin_to = int(coin_to) msg_buf.coin_to = int(coin_to)
msg_buf.amount_from = int(amount) msg_buf.amount_from = int(amount)
msg_buf.rate = int(rate) msg_buf.amount_to = int(amount_to)
msg_buf.min_bid_amount = int(min_bid_amount) msg_buf.min_bid_amount = int(min_bid_amount)
msg_buf.time_valid = valid_for_seconds msg_buf.time_valid = valid_for_seconds
@ -1644,7 +1647,8 @@ class BasicSwap(BaseApp):
coin_from=msg_buf.coin_from, coin_from=msg_buf.coin_from,
coin_to=msg_buf.coin_to, coin_to=msg_buf.coin_to,
amount_from=msg_buf.amount_from, amount_from=msg_buf.amount_from,
rate=msg_buf.rate, amount_to=msg_buf.amount_to,
rate=rate,
min_bid_amount=msg_buf.min_bid_amount, min_bid_amount=msg_buf.min_bid_amount,
time_valid=msg_buf.time_valid, time_valid=msg_buf.time_valid,
lock_type=int(msg_buf.lock_type), lock_type=int(msg_buf.lock_type),
@ -2264,24 +2268,34 @@ class BasicSwap(BaseApp):
valid_for_seconds = extra_options.get('valid_for_seconds', 60 * 10) valid_for_seconds = extra_options.get('valid_for_seconds', 60 * 10)
self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, valid_for_seconds) self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, valid_for_seconds)
bid_rate = extra_options.get('bid_rate', offer.rate) if not isinstance(amount, int):
amount = int(amount)
self.log.warning('postBid amount should be an integer type.')
coin_from = Coins(offer.coin_from)
coin_to = Coins(offer.coin_to)
ci_from = self.ci(coin_from)
ci_to = self.ci(coin_to)
if 'amount_to' in extra_options:
amount_to: int = extra_options['amount_to']
bid_rate: int = ci_from.make_int(amount_to / amount, r=1)
else:
bid_rate = extra_options.get('bid_rate', offer.rate)
amount_to: int = int((amount * bid_rate) // ci_from.COIN())
self.validateBidAmount(offer, amount, bid_rate) self.validateBidAmount(offer, amount, bid_rate)
self.mxDB.acquire() self.mxDB.acquire()
try: try:
self.checkCoinsReady(coin_from, coin_to)
msg_buf = BidMessage() msg_buf = BidMessage()
msg_buf.protocol_version = PROTOCOL_VERSION_SECRET_HASH msg_buf.protocol_version = PROTOCOL_VERSION_SECRET_HASH
msg_buf.offer_msg_id = offer_id msg_buf.offer_msg_id = offer_id
msg_buf.time_valid = valid_for_seconds msg_buf.time_valid = valid_for_seconds
msg_buf.amount = int(amount) # amount of coin_from msg_buf.amount = amount # amount of coin_from
msg_buf.rate = bid_rate msg_buf.amount_to = amount_to
coin_from = Coins(offer.coin_from)
coin_to = Coins(offer.coin_to)
ci_from = self.ci(coin_from)
ci_to = self.ci(coin_to)
self.checkCoinsReady(coin_from, coin_to)
amount_to = int((msg_buf.amount * bid_rate) // ci_from.COIN()) amount_to = int((msg_buf.amount * bid_rate) // ci_from.COIN())
@ -2314,14 +2328,14 @@ class BasicSwap(BaseApp):
bid_id=bid_id, bid_id=bid_id,
offer_id=offer_id, offer_id=offer_id,
amount=msg_buf.amount, amount=msg_buf.amount,
rate=msg_buf.rate, amount_to=msg_buf.amount_to,
rate=bid_rate,
pkhash_buyer=msg_buf.pkhash_buyer, pkhash_buyer=msg_buf.pkhash_buyer,
proof_address=msg_buf.proof_address, proof_address=msg_buf.proof_address,
proof_utxos=msg_buf.proof_utxos, proof_utxos=msg_buf.proof_utxos,
created_at=now, created_at=now,
contract_count=contract_count, contract_count=contract_count,
amount_to=amount_to,
expire_at=now + msg_buf.time_valid, expire_at=now + msg_buf.time_valid,
bid_addr=bid_addr, bid_addr=bid_addr,
was_sent=True, was_sent=True,
@ -2627,8 +2641,16 @@ class BasicSwap(BaseApp):
ci_to = self.ci(coin_to) ci_to = self.ci(coin_to)
valid_for_seconds: int = extra_options.get('valid_for_seconds', 60 * 10) valid_for_seconds: int = extra_options.get('valid_for_seconds', 60 * 10)
bid_rate: int = extra_options.get('bid_rate', offer.rate)
amount_to: int = int((int(amount) * bid_rate) // ci_from.COIN()) if 'amount_to' in extra_options:
amount_to: int = extra_options['amount_to']
bid_rate: int = ci_from.make_int(amount_to / amount, r=1)
elif 'bid_rate' in extra_options:
bid_rate: int = extra_options.get('bid_rate', offer.rate)
amount_to: int = int((int(amount) * bid_rate) // ci_from.COIN())
else:
amount_to: int = offer.amount_to
bid_rate: int = ci_from.make_int(amount_to / amount, r=1)
bid_created_at: int = self.getTime() bid_created_at: int = self.getTime()
if offer.swap_type != SwapTypes.XMR_SWAP: if offer.swap_type != SwapTypes.XMR_SWAP:
@ -2646,8 +2668,6 @@ class BasicSwap(BaseApp):
reverse_bid: bool = self.is_reverse_ads_bid(coin_from) reverse_bid: bool = self.is_reverse_ads_bid(coin_from)
if reverse_bid: if reverse_bid:
reversed_rate: int = ci_to.make_int(amount / amount_to, r=1) reversed_rate: int = ci_to.make_int(amount / amount_to, r=1)
amount_from: int = int((int(amount_to) * reversed_rate) // ci_to.COIN())
ensure(abs(amount_from - amount) < 20, 'invalid bid amount') # TODO: Tolerance?
msg_buf = ADSBidIntentMessage() msg_buf = ADSBidIntentMessage()
msg_buf.protocol_version = PROTOCOL_VERSION_ADAPTOR_SIG msg_buf.protocol_version = PROTOCOL_VERSION_ADAPTOR_SIG
@ -2655,7 +2675,6 @@ class BasicSwap(BaseApp):
msg_buf.time_valid = valid_for_seconds msg_buf.time_valid = valid_for_seconds
msg_buf.amount_from = amount msg_buf.amount_from = amount
msg_buf.amount_to = amount_to msg_buf.amount_to = amount_to
msg_buf.rate = bid_rate
bid_bytes = msg_buf.SerializeToString() bid_bytes = msg_buf.SerializeToString()
payload_hex = str.format('{:02x}', MessageTypes.ADS_BID_LF) + bid_bytes.hex() payload_hex = str.format('{:02x}', MessageTypes.ADS_BID_LF) + bid_bytes.hex()
@ -2673,10 +2692,10 @@ class BasicSwap(BaseApp):
bid_id=xmr_swap.bid_id, bid_id=xmr_swap.bid_id,
offer_id=offer_id, offer_id=offer_id,
amount=msg_buf.amount_to, amount=msg_buf.amount_to,
amount_to=msg_buf.amount_from,
rate=reversed_rate, rate=reversed_rate,
created_at=bid_created_at, created_at=bid_created_at,
contract_count=xmr_swap.contract_count, contract_count=xmr_swap.contract_count,
amount_to=msg_buf.amount_from,
expire_at=bid_created_at + msg_buf.time_valid, expire_at=bid_created_at + msg_buf.time_valid,
bid_addr=bid_addr, bid_addr=bid_addr,
was_sent=True, was_sent=True,
@ -2700,7 +2719,7 @@ class BasicSwap(BaseApp):
msg_buf.offer_msg_id = offer_id msg_buf.offer_msg_id = offer_id
msg_buf.time_valid = valid_for_seconds msg_buf.time_valid = valid_for_seconds
msg_buf.amount = int(amount) # Amount of coin_from msg_buf.amount = int(amount) # Amount of coin_from
msg_buf.rate = bid_rate msg_buf.amount_to = amount_to
address_out = self.getReceiveAddressFromPool(coin_from, offer_id, TxTypes.XMR_SWAP_A_LOCK) address_out = self.getReceiveAddressFromPool(coin_from, offer_id, TxTypes.XMR_SWAP_A_LOCK)
if coin_from == Coins.PART_BLIND: if coin_from == Coins.PART_BLIND:
@ -2763,10 +2782,10 @@ class BasicSwap(BaseApp):
bid_id=xmr_swap.bid_id, bid_id=xmr_swap.bid_id,
offer_id=offer_id, offer_id=offer_id,
amount=msg_buf.amount, amount=msg_buf.amount,
rate=msg_buf.rate, amount_to=msg_buf.amount_to,
rate=bid_rate,
created_at=bid_created_at, created_at=bid_created_at,
contract_count=xmr_swap.contract_count, contract_count=xmr_swap.contract_count,
amount_to=(msg_buf.amount * msg_buf.rate) // ci_from.COIN(),
expire_at=bid_created_at + msg_buf.time_valid, expire_at=bid_created_at + msg_buf.time_valid,
bid_addr=bid_addr, bid_addr=bid_addr,
was_sent=True, was_sent=True,
@ -4537,12 +4556,13 @@ class BasicSwap(BaseApp):
ensure(offer_data.coin_from != offer_data.coin_to, 'coin_from == coin_to') ensure(offer_data.coin_from != offer_data.coin_to, 'coin_from == coin_to')
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.amount_to, offer_data.min_bid_amount)
self.validateOfferLockValue(offer_data.swap_type, coin_from, coin_to, offer_data.lock_type, offer_data.lock_value) self.validateOfferLockValue(offer_data.swap_type, coin_from, coin_to, offer_data.lock_type, offer_data.lock_value)
self.validateOfferValidTime(offer_data.swap_type, coin_from, coin_to, offer_data.time_valid) self.validateOfferValidTime(offer_data.swap_type, coin_from, coin_to, offer_data.time_valid)
ensure(msg['sent'] + offer_data.time_valid >= now, 'Offer expired') ensure(msg['sent'] + offer_data.time_valid >= now, 'Offer expired')
offer_rate: int = ci_from.make_int(offer_data.amount_to / offer_data.amount_from, r=1)
reverse_bid: bool = self.is_reverse_ads_bid(coin_from) reverse_bid: bool = self.is_reverse_ads_bid(coin_from)
if offer_data.swap_type == SwapTypes.SELLER_FIRST: if offer_data.swap_type == SwapTypes.SELLER_FIRST:
@ -4593,7 +4613,8 @@ class BasicSwap(BaseApp):
coin_from=offer_data.coin_from, coin_from=offer_data.coin_from,
coin_to=offer_data.coin_to, coin_to=offer_data.coin_to,
amount_from=offer_data.amount_from, amount_from=offer_data.amount_from,
rate=offer_data.rate, amount_to=offer_data.amount_to,
rate=offer_rate,
min_bid_amount=offer_data.min_bid_amount, min_bid_amount=offer_data.min_bid_amount,
time_valid=offer_data.time_valid, time_valid=offer_data.time_valid,
lock_type=int(offer_data.lock_type), lock_type=int(offer_data.lock_type),
@ -4806,16 +4827,16 @@ class BasicSwap(BaseApp):
ensure(now <= offer.expire_at, 'Offer expired') ensure(now <= offer.expire_at, 'Offer expired')
self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, bid_data.time_valid) self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, bid_data.time_valid)
ensure(now <= msg['sent'] + bid_data.time_valid, 'Bid expired') ensure(now <= msg['sent'] + bid_data.time_valid, 'Bid expired')
self.validateBidAmount(offer, bid_data.amount, bid_data.rate)
# TODO: Allow higher bids
# assert (bid_data.rate != offer['data'].rate), 'Bid rate mismatch'
coin_to = Coins(offer.coin_to) coin_to = Coins(offer.coin_to)
ci_from = self.ci(offer.coin_from) ci_from = self.ci(offer.coin_from)
ci_to = self.ci(coin_to) ci_to = self.ci(coin_to)
bid_rate: int = ci_from.make_int(bid_data.amount_to / bid_data.amount, r=1)
self.validateBidAmount(offer, bid_data.amount, bid_rate)
# TODO: Allow higher bids
# assert (bid_data.rate != offer['data'].rate), 'Bid rate mismatch'
amount_to = int((bid_data.amount * bid_data.rate) // ci_from.COIN())
swap_type = offer.swap_type swap_type = offer.swap_type
if swap_type == SwapTypes.SELLER_FIRST: if swap_type == SwapTypes.SELLER_FIRST:
ensure(len(bid_data.pkhash_buyer) == 20, 'Bad pkhash_buyer length') ensure(len(bid_data.pkhash_buyer) == 20, 'Bad pkhash_buyer length')
@ -4823,7 +4844,7 @@ class BasicSwap(BaseApp):
proof_utxos = ci_to.decodeProofUtxos(bid_data.proof_utxos) proof_utxos = ci_to.decodeProofUtxos(bid_data.proof_utxos)
sum_unspent = ci_to.verifyProofOfFunds(bid_data.proof_address, bid_data.proof_signature, proof_utxos, offer_id) sum_unspent = ci_to.verifyProofOfFunds(bid_data.proof_address, bid_data.proof_signature, proof_utxos, offer_id)
self.log.debug('Proof of funds %s %s', bid_data.proof_address, self.ci(coin_to).format_amount(sum_unspent)) self.log.debug('Proof of funds %s %s', bid_data.proof_address, self.ci(coin_to).format_amount(sum_unspent))
ensure(sum_unspent >= amount_to, 'Proof of funds failed') ensure(sum_unspent >= bid_data.amount_to, 'Proof of funds failed')
elif swap_type == SwapTypes.BUYER_FIRST: elif swap_type == SwapTypes.BUYER_FIRST:
raise ValueError('TODO') raise ValueError('TODO')
@ -4840,13 +4861,13 @@ class BasicSwap(BaseApp):
offer_id=offer_id, offer_id=offer_id,
protocol_version=bid_data.protocol_version, protocol_version=bid_data.protocol_version,
amount=bid_data.amount, amount=bid_data.amount,
rate=bid_data.rate, amount_to=bid_data.amount_to,
rate=bid_rate,
pkhash_buyer=bid_data.pkhash_buyer, pkhash_buyer=bid_data.pkhash_buyer,
proof_address=bid_data.proof_address, proof_address=bid_data.proof_address,
proof_utxos=bid_data.proof_utxos, proof_utxos=bid_data.proof_utxos,
created_at=msg['sent'], created_at=msg['sent'],
amount_to=amount_to,
expire_at=msg['sent'] + bid_data.time_valid, expire_at=msg['sent'] + bid_data.time_valid,
bid_addr=msg['from'], bid_addr=msg['from'],
was_received=True, was_received=True,
@ -5116,7 +5137,8 @@ class BasicSwap(BaseApp):
self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, bid_data.time_valid) self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, bid_data.time_valid)
ensure(now <= msg['sent'] + bid_data.time_valid, 'Bid expired') ensure(now <= msg['sent'] + bid_data.time_valid, 'Bid expired')
self.validateBidAmount(offer, bid_data.amount, bid_data.rate) bid_rate: int = ci_from.make_int(bid_data.amount_to / bid_data.amount, r=1)
self.validateBidAmount(offer, bid_data.amount, bid_rate)
ensure(ci_to.verifyKey(bid_data.kbvf), 'Invalid chain B follower view key') ensure(ci_to.verifyKey(bid_data.kbvf), 'Invalid chain B follower view key')
ensure(ci_from.verifyPubkey(bid_data.pkaf), 'Invalid chain A follower public key') ensure(ci_from.verifyPubkey(bid_data.pkaf), 'Invalid chain A follower public key')
@ -5135,9 +5157,9 @@ class BasicSwap(BaseApp):
offer_id=offer_id, offer_id=offer_id,
protocol_version=bid_data.protocol_version, protocol_version=bid_data.protocol_version,
amount=bid_data.amount, amount=bid_data.amount,
rate=bid_data.rate, amount_to=bid_data.amount_to,
rate=bid_rate,
created_at=msg['sent'], created_at=msg['sent'],
amount_to=(bid_data.amount * bid_data.rate) // ci_from.COIN(),
expire_at=msg['sent'] + bid_data.time_valid, expire_at=msg['sent'] + bid_data.time_valid,
bid_addr=msg['from'], bid_addr=msg['from'],
was_received=True, was_received=True,
@ -5992,14 +6014,10 @@ class BasicSwap(BaseApp):
self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, bid_data.time_valid) self.validateBidValidTime(offer.swap_type, offer.coin_from, offer.coin_to, bid_data.time_valid)
ensure(now <= msg['sent'] + bid_data.time_valid, 'Bid expired') ensure(now <= msg['sent'] + bid_data.time_valid, 'Bid expired')
amount_from: int = bid_data.amount_from # ci_from/to are reversed
amount_to: int = (bid_data.amount_from * bid_data.rate) // ci_to.COIN() bid_rate: int = ci_to.make_int(bid_data.amount_to / bid_data.amount_from, r=1)
ensure(abs(amount_to - bid_data.amount_to) < 20, 'invalid bid amount_to') # TODO: Tolerance? reversed_rate: int = ci_from.make_int(bid_data.amount_from / bid_data.amount_to, r=1)
reversed_rate: int = ci_from.make_int(amount_from / bid_data.amount_to, r=1) self.validateBidAmount(offer, bid_data.amount_from, bid_rate)
amount_from_recovered: int = int((amount_to * reversed_rate) // ci_from.COIN())
ensure(abs(amount_from - amount_from_recovered) < 20, 'invalid bid amount_from') # TODO: Tolerance?
self.validateBidAmount(offer, amount_from, bid_data.rate)
bid_id = bytes.fromhex(msg['msgid']) bid_id = bytes.fromhex(msg['msgid'])
@ -6010,10 +6028,10 @@ class BasicSwap(BaseApp):
bid_id=bid_id, bid_id=bid_id,
offer_id=offer_id, offer_id=offer_id,
protocol_version=bid_data.protocol_version, protocol_version=bid_data.protocol_version,
amount=amount_to, amount=bid_data.amount_to,
amount_to=bid_data.amount_from,
rate=reversed_rate, rate=reversed_rate,
created_at=msg['sent'], created_at=msg['sent'],
amount_to=amount_from,
expire_at=msg['sent'] + bid_data.time_valid, expire_at=msg['sent'] + bid_data.time_valid,
bid_addr=msg['from'], bid_addr=msg['from'],
was_sent=False, was_sent=False,
@ -6044,7 +6062,7 @@ class BasicSwap(BaseApp):
session = self.openSession() session = self.openSession()
self.notify(NT.BID_RECEIVED, {'type': 'ads_reversed', 'bid_id': bid.bid_id.hex(), 'offer_id': bid.offer_id.hex()}, session) self.notify(NT.BID_RECEIVED, {'type': 'ads_reversed', 'bid_id': bid.bid_id.hex(), 'offer_id': bid.offer_id.hex()}, session)
options = {'reverse_bid': True, 'bid_rate': bid_data.rate} options = {'reverse_bid': True, 'bid_rate': bid_rate}
if self.shouldAutoAcceptBid(offer, bid, session, options=options): if self.shouldAutoAcceptBid(offer, bid, session, options=options):
delay = self.get_delay_event_seconds() delay = self.get_delay_event_seconds()
self.log.info('Auto accepting reverse adaptor-sig bid %s in %d seconds', bid.bid_id.hex(), delay) self.log.info('Auto accepting reverse adaptor-sig bid %s in %d seconds', bid.bid_id.hex(), delay)
@ -6998,6 +7016,7 @@ class BasicSwap(BaseApp):
result[13] = amount_to result[13] = amount_to
ci_from = self.ci(coin_from) ci_from = self.ci(coin_from)
result[10] = ci_from.make_int(amount_to / amount_from, r=1) result[10] = ci_from.make_int(amount_to / amount_from, r=1)
rv.append(result) rv.append(result)
return rv return rv
finally: finally:

@ -11,7 +11,7 @@ from enum import IntEnum, auto
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
CURRENT_DB_VERSION = 22 CURRENT_DB_VERSION = 23
CURRENT_DB_DATA_VERSION = 4 CURRENT_DB_DATA_VERSION = 4
Base = declarative_base() Base = declarative_base()
@ -61,6 +61,7 @@ class Offer(Base):
coin_from = sa.Column(sa.Integer) coin_from = sa.Column(sa.Integer)
coin_to = sa.Column(sa.Integer) coin_to = sa.Column(sa.Integer)
amount_from = sa.Column(sa.BigInteger) amount_from = sa.Column(sa.BigInteger)
amount_to = sa.Column(sa.BigInteger)
rate = sa.Column(sa.BigInteger) rate = sa.Column(sa.BigInteger)
min_bid_amount = sa.Column(sa.BigInteger) min_bid_amount = sa.Column(sa.BigInteger)
time_valid = sa.Column(sa.BigInteger) time_valid = sa.Column(sa.BigInteger)

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2022-2023 tecnovert # Copyright (c) 2022-2024 tecnovert
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php. # file LICENSE or http://www.opensource.org/licenses/mit-license.php.
@ -297,7 +297,9 @@ def upgradeDatabase(self, db_version):
db_version += 1 db_version += 1
session.execute('ALTER TABLE offers ADD COLUMN proof_utxos BLOB') session.execute('ALTER TABLE offers ADD COLUMN proof_utxos BLOB')
session.execute('ALTER TABLE bids ADD COLUMN proof_utxos BLOB') session.execute('ALTER TABLE bids ADD COLUMN proof_utxos BLOB')
elif current_version == 22:
db_version += 1
session.execute('ALTER TABLE offers ADD COLUMN amount_to INTEGER')
if current_version != db_version: if current_version != db_version:
self.db_version = db_version self.db_version = db_version
self.setIntKVInSession('db_version', db_version, session) self.setIntKVInSession('db_version', db_version, session)

@ -339,7 +339,10 @@ def js_bids(self, url_split, post_string: str, is_json: bool) -> bytes:
extra_options = { extra_options = {
'valid_for_seconds': valid_for_seconds, 'valid_for_seconds': valid_for_seconds,
} }
if have_data_entry(post_data, 'bid_rate'):
if have_data_entry(post_data, 'amount_to'):
extra_options['amount_to'] = inputAmount(get_data_entry(post_data, 'amount_to'), ci_to)
elif have_data_entry(post_data, 'bid_rate'):
extra_options['bid_rate'] = ci_to.make_int(get_data_entry(post_data, 'bid_rate'), r=1) extra_options['bid_rate'] = ci_to.make_int(get_data_entry(post_data, 'bid_rate'), r=1)
if have_data_entry(post_data, 'bid_amount'): if have_data_entry(post_data, 'bid_amount'):
amount_from = inputAmount(get_data_entry(post_data, 'bid_amount'), ci_from) amount_from = inputAmount(get_data_entry(post_data, 'bid_amount'), ci_from)
@ -493,10 +496,10 @@ def js_rate(self, url_split, post_string, is_json) -> bytes:
amount_from = ci_from.format_amount(int((amt_to * rate) // ci_to.COIN()), r=1) amount_from = ci_from.format_amount(int((amt_to * rate) // ci_to.COIN()), r=1)
return bytes(json.dumps({'amount_from': amount_from}), 'UTF-8') return bytes(json.dumps({'amount_from': amount_from}), 'UTF-8')
amt_from = inputAmount(get_data_entry(post_data, 'amt_from'), ci_from) amt_from: int = inputAmount(get_data_entry(post_data, 'amt_from'), ci_from)
amt_to = inputAmount(get_data_entry(post_data, 'amt_to'), ci_to) amt_to: int = inputAmount(get_data_entry(post_data, 'amt_to'), ci_to)
rate = ci_to.format_amount(ci_from.make_int(amt_to / amt_from, r=1)) rate: int = ci_to.format_amount(ci_from.make_int(amt_to / amt_from, r=1))
return bytes(json.dumps({'rate': rate}), 'UTF-8') return bytes(json.dumps({'rate': rate}), 'UTF-8')

@ -4,12 +4,13 @@ package basicswap;
/* Step 1, seller -> network */ /* Step 1, seller -> network */
message OfferMessage { message OfferMessage {
uint32 coin_from = 1; uint32 protocol_version = 1;
uint32 coin_to = 2; uint32 coin_from = 2;
uint64 amount_from = 3; uint32 coin_to = 3;
uint64 rate = 4; uint64 amount_from = 4;
uint64 min_bid_amount = 5; uint64 amount_to = 5;
uint64 time_valid = 6; uint64 min_bid_amount = 6;
uint64 time_valid = 7;
enum LockType { enum LockType {
NOT_SET = 0; NOT_SET = 0;
SEQUENCE_LOCK_BLOCKS = 1; SEQUENCE_LOCK_BLOCKS = 1;
@ -17,20 +18,19 @@ message OfferMessage {
ABS_LOCK_BLOCKS = 3; ABS_LOCK_BLOCKS = 3;
ABS_LOCK_TIME = 4; ABS_LOCK_TIME = 4;
} }
LockType lock_type = 7; LockType lock_type = 8;
uint32 lock_value = 8; uint32 lock_value = 9;
uint32 swap_type = 9; uint32 swap_type = 10;
/* optional */ /* optional */
string proof_address = 10; string proof_address = 11;
string proof_signature = 11; string proof_signature = 12;
bytes pkhash_seller = 12; bytes pkhash_seller = 13;
bytes secret_hash = 13; bytes secret_hash = 14;
uint64 fee_rate_from = 14; uint64 fee_rate_from = 15;
uint64 fee_rate_to = 15; uint64 fee_rate_to = 16;
uint32 protocol_version = 16;
bool amount_negotiable = 17; bool amount_negotiable = 17;
bool rate_negotiable = 18; bool rate_negotiable = 18;
@ -39,34 +39,27 @@ message OfferMessage {
/* Step 2, buyer -> seller */ /* Step 2, buyer -> seller */
message BidMessage { message BidMessage {
bytes offer_msg_id = 1; uint32 protocol_version = 1;
uint64 time_valid = 2; /* seconds bid is valid for */ bytes offer_msg_id = 2;
uint64 amount = 3; /* amount of amount_from bid is for */ uint64 time_valid = 3; /* seconds bid is valid for */
uint64 rate = 4; uint64 amount = 4; /* amount of amount_from bid is for */
bytes pkhash_buyer = 5; /* buyer's address to receive amount_from */ uint64 amount_to = 5;
string proof_address = 6; bytes pkhash_buyer = 6; /* buyer's address to receive amount_from */
string proof_signature = 7; string proof_address = 7;
string proof_signature = 8;
uint32 protocol_version = 8;
bytes proof_utxos = 9; /* 32 byte txid 2 byte vout, repeated */ bytes proof_utxos = 9; /* 32 byte txid 2 byte vout, repeated */
} }
/* For tests */ /* For tests */
message BidMessage_v1Deprecated { message BidMessage_test {
bytes offer_msg_id = 1; uint32 protocol_version = 1;
uint64 time_valid = 2; /* seconds bid is valid for */ bytes offer_msg_id = 2;
uint64 amount = 3; /* amount of amount_from bid is for */ uint64 time_valid = 3;
uint64 rate = 4; uint64 amount = 4;
bytes pkhash_buyer = 5; /* buyer's address to receive amount_from */ uint64 rate = 5;
string proof_address = 6;
string proof_signature = 7;
uint32 protocol_version = 8;
} }
/* Step 3, seller -> buyer */ /* Step 3, seller -> buyer */
message BidAcceptMessage { message BidAcceptMessage {
bytes bid_msg_id = 1; bytes bid_msg_id = 1;
@ -81,25 +74,23 @@ message OfferRevokeMessage {
message BidRejectMessage { message BidRejectMessage {
bytes bid_msg_id = 1; bytes bid_msg_id = 1;
uint32 reject_code = 2; uint32 reject_code = 2;
} }
message XmrBidMessage { message XmrBidMessage {
/* MSG1L, F -> L */ /* MSG1L, F -> L */
bytes offer_msg_id = 1; uint32 protocol_version = 1;
uint64 time_valid = 2; /* seconds bid is valid for */ bytes offer_msg_id = 2;
uint64 amount = 3; /* amount of amount_from bid is for */ uint64 time_valid = 3; /* seconds bid is valid for */
uint64 rate = 4; uint64 amount = 4; /* amount of amount_from bid is for */
uint64 amount_to = 5;
bytes pkaf = 5; bytes pkaf = 6;
bytes kbvf = 6; bytes kbvf = 7;
bytes kbsf_dleag = 7; bytes kbsf_dleag = 8;
bytes dest_af = 8; bytes dest_af = 9;
uint32 protocol_version = 9;
} }
message XmrSplitMessage { message XmrSplitMessage {
@ -112,24 +103,22 @@ message XmrSplitMessage {
message XmrBidAcceptMessage { message XmrBidAcceptMessage {
bytes bid_msg_id = 1; bytes bid_msg_id = 1;
bytes pkal = 3; bytes pkal = 2;
bytes kbvl = 3;
bytes kbvl = 4; bytes kbsl_dleag = 4;
bytes kbsl_dleag = 5;
/* MSG2F */ /* MSG2F */
bytes a_lock_tx = 6; bytes a_lock_tx = 5;
bytes a_lock_tx_script = 7; bytes a_lock_tx_script = 6;
bytes a_lock_refund_tx = 8; bytes a_lock_refund_tx = 7;
bytes a_lock_refund_tx_script = 9; bytes a_lock_refund_tx_script = 8;
bytes a_lock_refund_spend_tx = 10; bytes a_lock_refund_spend_tx = 9;
bytes al_lock_refund_tx_sig = 11; bytes al_lock_refund_tx_sig = 10;
} }
message XmrBidLockTxSigsMessage { message XmrBidLockTxSigsMessage {
/* MSG3L */ /* MSG3L */
bytes bid_msg_id = 1; bytes bid_msg_id = 1;
bytes af_lock_refund_spend_tx_esig = 2; bytes af_lock_refund_spend_tx_esig = 2;
bytes af_lock_refund_tx_sig = 3; bytes af_lock_refund_tx_sig = 3;
} }
@ -137,7 +126,6 @@ message XmrBidLockTxSigsMessage {
message XmrBidLockSpendTxMessage { message XmrBidLockSpendTxMessage {
/* MSG4F */ /* MSG4F */
bytes bid_msg_id = 1; bytes bid_msg_id = 1;
bytes a_lock_spend_tx = 2; bytes a_lock_spend_tx = 2;
bytes kal_sig = 3; bytes kal_sig = 3;
} }
@ -145,19 +133,16 @@ message XmrBidLockSpendTxMessage {
message XmrBidLockReleaseMessage { message XmrBidLockReleaseMessage {
/* MSG5F */ /* MSG5F */
bytes bid_msg_id = 1; bytes bid_msg_id = 1;
bytes al_lock_spend_tx_esig = 2; bytes al_lock_spend_tx_esig = 2;
} }
message ADSBidIntentMessage { message ADSBidIntentMessage {
/* L -> F Sent from bidder, construct a reverse bid */ /* L -> F Sent from bidder, construct a reverse bid */
bytes offer_msg_id = 1; uint32 protocol_version = 1;
uint64 time_valid = 2; /* seconds bid is valid for */ bytes offer_msg_id = 2;
uint64 amount_from = 3; /* amount of offer.coin_from bid is for */ uint64 time_valid = 3; /* seconds bid is valid for */
uint64 amount_to = 4; /* amount of offer.coin_to bid is for, equivalent to bid.amount */ uint64 amount_from = 4; /* amount of offer.coin_from bid is for */
uint64 rate = 5; /* amount of offer.coin_from bid is for */ uint64 amount_to = 5; /* amount of offer.coin_to bid is for, equivalent to bid.amount */
uint32 protocol_version = 6;
} }
message ADSBidIntentAcceptMessage { message ADSBidIntentAcceptMessage {

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT! # Generated by the protocol buffer compiler. DO NOT EDIT!
# source: messages.proto # source: messages.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code.""" """Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool from google.protobuf import descriptor_pool as _descriptor_pool
@ -13,7 +14,7 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0emessages.proto\x12\tbasicswap\"\xa6\x04\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\x12\x18\n\x10protocol_version\x18\x10 \x01(\r\x12\x19\n\x11\x61mount_negotiable\x18\x11 \x01(\x08\x12\x17\n\x0frate_negotiable\x18\x12 \x01(\x08\"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\"\xc9\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\x0c\n\x04rate\x18\x04 \x01(\x04\x12\x14\n\x0cpkhash_buyer\x18\x05 \x01(\x0c\x12\x15\n\rproof_address\x18\x06 \x01(\t\x12\x17\n\x0fproof_signature\x18\x07 \x01(\t\x12\x18\n\x10protocol_version\x18\x08 \x01(\r\x12\x13\n\x0bproof_utxos\x18\t \x01(\x0c\"\xc1\x01\n\x17\x42idMessage_v1Deprecated\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\x04rate\x18\x04 \x01(\x04\x12\x14\n\x0cpkhash_buyer\x18\x05 \x01(\x0c\x12\x15\n\rproof_address\x18\x06 \x01(\t\x12\x17\n\x0fproof_signature\x18\x07 \x01(\t\x12\x18\n\x10protocol_version\x18\x08 \x01(\r\"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\"=\n\x12OfferRevokeMessage\x12\x14\n\x0coffer_msg_id\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\";\n\x10\x42idRejectMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x13\n\x0breject_code\x18\x02 \x01(\r\"\xb2\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\x04rate\x18\x04 \x01(\x04\x12\x0c\n\x04pkaf\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\x12\x18\n\x10protocol_version\x18\t \x01(\r\"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\"\x80\x02\n\x13XmrBidAcceptMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x0c\n\x04pkal\x18\x03 \x01(\x0c\x12\x0c\n\x04kbvl\x18\x04 \x01(\x0c\x12\x12\n\nkbsl_dleag\x18\x05 \x01(\x0c\x12\x11\n\ta_lock_tx\x18\x06 \x01(\x0c\x12\x18\n\x10\x61_lock_tx_script\x18\x07 \x01(\x0c\x12\x18\n\x10\x61_lock_refund_tx\x18\x08 \x01(\x0c\x12\x1f\n\x17\x61_lock_refund_tx_script\x18\t \x01(\x0c\x12\x1e\n\x16\x61_lock_refund_spend_tx\x18\n \x01(\x0c\x12\x1d\n\x15\x61l_lock_refund_tx_sig\x18\x0b \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\"X\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\x0f\n\x07kal_sig\x18\x03 \x01(\x0c\"M\n\x18XmrBidLockReleaseMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x1d\n\x15\x61l_lock_spend_tx_esig\x18\x02 \x01(\x0c\"\x8f\x01\n\x13\x41\x44SBidIntentMessage\x12\x14\n\x0coffer_msg_id\x18\x01 \x01(\x0c\x12\x12\n\ntime_valid\x18\x02 \x01(\x04\x12\x13\n\x0b\x61mount_from\x18\x03 \x01(\x04\x12\x11\n\tamount_to\x18\x04 \x01(\x04\x12\x0c\n\x04rate\x18\x05 \x01(\x04\x12\x18\n\x10protocol_version\x18\x06 \x01(\r\"p\n\x19\x41\x44SBidIntentAcceptMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x0c\n\x04pkaf\x18\x02 \x01(\x0c\x12\x0c\n\x04kbvf\x18\x03 \x01(\x0c\x12\x12\n\nkbsf_dleag\x18\x04 \x01(\x0c\x12\x0f\n\x07\x64\x65st_af\x18\x05 \x01(\x0c\x62\x06proto3') DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0emessages.proto\x12\tbasicswap\"\xc0\x04\n\x0cOfferMessage\x12\x18\n\x10protocol_version\x18\x01 \x01(\r\x12\x11\n\tcoin_from\x18\x02 \x01(\r\x12\x0f\n\x07\x63oin_to\x18\x03 \x01(\r\x12\x13\n\x0b\x61mount_from\x18\x04 \x01(\x04\x12\x11\n\tamount_to\x18\x05 \x01(\x04\x12\x16\n\x0emin_bid_amount\x18\x06 \x01(\x04\x12\x12\n\ntime_valid\x18\x07 \x01(\x04\x12\x33\n\tlock_type\x18\x08 \x01(\x0e\x32 .basicswap.OfferMessage.LockType\x12\x12\n\nlock_value\x18\t \x01(\r\x12\x11\n\tswap_type\x18\n \x01(\r\x12\x15\n\rproof_address\x18\x0b \x01(\t\x12\x17\n\x0fproof_signature\x18\x0c \x01(\t\x12\x15\n\rpkhash_seller\x18\r \x01(\x0c\x12\x13\n\x0bsecret_hash\x18\x0e \x01(\x0c\x12\x15\n\rfee_rate_from\x18\x0f \x01(\x04\x12\x13\n\x0b\x66\x65\x65_rate_to\x18\x10 \x01(\x04\x12\x19\n\x11\x61mount_negotiable\x18\x11 \x01(\x08\x12\x17\n\x0frate_negotiable\x18\x12 \x01(\x08\x12\x13\n\x0bproof_utxos\x18\x13 \x01(\x0c\"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\"\xce\x01\n\nBidMessage\x12\x18\n\x10protocol_version\x18\x01 \x01(\r\x12\x14\n\x0coffer_msg_id\x18\x02 \x01(\x0c\x12\x12\n\ntime_valid\x18\x03 \x01(\x04\x12\x0e\n\x06\x61mount\x18\x04 \x01(\x04\x12\x11\n\tamount_to\x18\x05 \x01(\x04\x12\x14\n\x0cpkhash_buyer\x18\x06 \x01(\x0c\x12\x15\n\rproof_address\x18\x07 \x01(\t\x12\x17\n\x0fproof_signature\x18\x08 \x01(\t\x12\x13\n\x0bproof_utxos\x18\t \x01(\x0c\"s\n\x0f\x42idMessage_test\x12\x18\n\x10protocol_version\x18\x01 \x01(\r\x12\x14\n\x0coffer_msg_id\x18\x02 \x01(\x0c\x12\x12\n\ntime_valid\x18\x03 \x01(\x04\x12\x0e\n\x06\x61mount\x18\x04 \x01(\x04\x12\x0c\n\x04rate\x18\x05 \x01(\x04\"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\"=\n\x12OfferRevokeMessage\x12\x14\n\x0coffer_msg_id\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\";\n\x10\x42idRejectMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x13\n\x0breject_code\x18\x02 \x01(\r\"\xb7\x01\n\rXmrBidMessage\x12\x18\n\x10protocol_version\x18\x01 \x01(\r\x12\x14\n\x0coffer_msg_id\x18\x02 \x01(\x0c\x12\x12\n\ntime_valid\x18\x03 \x01(\x04\x12\x0e\n\x06\x61mount\x18\x04 \x01(\x04\x12\x11\n\tamount_to\x18\x05 \x01(\x04\x12\x0c\n\x04pkaf\x18\x06 \x01(\x0c\x12\x0c\n\x04kbvf\x18\x07 \x01(\x0c\x12\x12\n\nkbsf_dleag\x18\x08 \x01(\x0c\x12\x0f\n\x07\x64\x65st_af\x18\t \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\"\x80\x02\n\x13XmrBidAcceptMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x0c\n\x04pkal\x18\x02 \x01(\x0c\x12\x0c\n\x04kbvl\x18\x03 \x01(\x0c\x12\x12\n\nkbsl_dleag\x18\x04 \x01(\x0c\x12\x11\n\ta_lock_tx\x18\x05 \x01(\x0c\x12\x18\n\x10\x61_lock_tx_script\x18\x06 \x01(\x0c\x12\x18\n\x10\x61_lock_refund_tx\x18\x07 \x01(\x0c\x12\x1f\n\x17\x61_lock_refund_tx_script\x18\x08 \x01(\x0c\x12\x1e\n\x16\x61_lock_refund_spend_tx\x18\t \x01(\x0c\x12\x1d\n\x15\x61l_lock_refund_tx_sig\x18\n \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\"X\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\x0f\n\x07kal_sig\x18\x03 \x01(\x0c\"M\n\x18XmrBidLockReleaseMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x1d\n\x15\x61l_lock_spend_tx_esig\x18\x02 \x01(\x0c\"\x81\x01\n\x13\x41\x44SBidIntentMessage\x12\x18\n\x10protocol_version\x18\x01 \x01(\r\x12\x14\n\x0coffer_msg_id\x18\x02 \x01(\x0c\x12\x12\n\ntime_valid\x18\x03 \x01(\x04\x12\x13\n\x0b\x61mount_from\x18\x04 \x01(\x04\x12\x11\n\tamount_to\x18\x05 \x01(\x04\"p\n\x19\x41\x44SBidIntentAcceptMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x0c\n\x04pkaf\x18\x02 \x01(\x0c\x12\x0c\n\x04kbvf\x18\x03 \x01(\x0c\x12\x12\n\nkbsf_dleag\x18\x04 \x01(\x0c\x12\x0f\n\x07\x64\x65st_af\x18\x05 \x01(\x0c\x62\x06proto3')
_globals = globals() _globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@ -21,33 +22,33 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'messages_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False: if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None DESCRIPTOR._options = None
_globals['_OFFERMESSAGE']._serialized_start=30 _globals['_OFFERMESSAGE']._serialized_start=30
_globals['_OFFERMESSAGE']._serialized_end=580 _globals['_OFFERMESSAGE']._serialized_end=606
_globals['_OFFERMESSAGE_LOCKTYPE']._serialized_start=467 _globals['_OFFERMESSAGE_LOCKTYPE']._serialized_start=493
_globals['_OFFERMESSAGE_LOCKTYPE']._serialized_end=580 _globals['_OFFERMESSAGE_LOCKTYPE']._serialized_end=606
_globals['_BIDMESSAGE']._serialized_start=583 _globals['_BIDMESSAGE']._serialized_start=609
_globals['_BIDMESSAGE']._serialized_end=784 _globals['_BIDMESSAGE']._serialized_end=815
_globals['_BIDMESSAGE_V1DEPRECATED']._serialized_start=787 _globals['_BIDMESSAGE_TEST']._serialized_start=817
_globals['_BIDMESSAGE_V1DEPRECATED']._serialized_end=980 _globals['_BIDMESSAGE_TEST']._serialized_end=932
_globals['_BIDACCEPTMESSAGE']._serialized_start=982 _globals['_BIDACCEPTMESSAGE']._serialized_start=934
_globals['_BIDACCEPTMESSAGE']._serialized_end=1068 _globals['_BIDACCEPTMESSAGE']._serialized_end=1020
_globals['_OFFERREVOKEMESSAGE']._serialized_start=1070 _globals['_OFFERREVOKEMESSAGE']._serialized_start=1022
_globals['_OFFERREVOKEMESSAGE']._serialized_end=1131 _globals['_OFFERREVOKEMESSAGE']._serialized_end=1083
_globals['_BIDREJECTMESSAGE']._serialized_start=1133 _globals['_BIDREJECTMESSAGE']._serialized_start=1085
_globals['_BIDREJECTMESSAGE']._serialized_end=1192 _globals['_BIDREJECTMESSAGE']._serialized_end=1144
_globals['_XMRBIDMESSAGE']._serialized_start=1195 _globals['_XMRBIDMESSAGE']._serialized_start=1147
_globals['_XMRBIDMESSAGE']._serialized_end=1373 _globals['_XMRBIDMESSAGE']._serialized_end=1330
_globals['_XMRSPLITMESSAGE']._serialized_start=1375 _globals['_XMRSPLITMESSAGE']._serialized_start=1332
_globals['_XMRSPLITMESSAGE']._serialized_end=1459 _globals['_XMRSPLITMESSAGE']._serialized_end=1416
_globals['_XMRBIDACCEPTMESSAGE']._serialized_start=1462 _globals['_XMRBIDACCEPTMESSAGE']._serialized_start=1419
_globals['_XMRBIDACCEPTMESSAGE']._serialized_end=1718 _globals['_XMRBIDACCEPTMESSAGE']._serialized_end=1675
_globals['_XMRBIDLOCKTXSIGSMESSAGE']._serialized_start=1720 _globals['_XMRBIDLOCKTXSIGSMESSAGE']._serialized_start=1677
_globals['_XMRBIDLOCKTXSIGSMESSAGE']._serialized_end=1834 _globals['_XMRBIDLOCKTXSIGSMESSAGE']._serialized_end=1791
_globals['_XMRBIDLOCKSPENDTXMESSAGE']._serialized_start=1836 _globals['_XMRBIDLOCKSPENDTXMESSAGE']._serialized_start=1793
_globals['_XMRBIDLOCKSPENDTXMESSAGE']._serialized_end=1924 _globals['_XMRBIDLOCKSPENDTXMESSAGE']._serialized_end=1881
_globals['_XMRBIDLOCKRELEASEMESSAGE']._serialized_start=1926 _globals['_XMRBIDLOCKRELEASEMESSAGE']._serialized_start=1883
_globals['_XMRBIDLOCKRELEASEMESSAGE']._serialized_end=2003 _globals['_XMRBIDLOCKRELEASEMESSAGE']._serialized_end=1960
_globals['_ADSBIDINTENTMESSAGE']._serialized_start=2006 _globals['_ADSBIDINTENTMESSAGE']._serialized_start=1963
_globals['_ADSBIDINTENTMESSAGE']._serialized_end=2149 _globals['_ADSBIDINTENTMESSAGE']._serialized_end=2092
_globals['_ADSBIDINTENTACCEPTMESSAGE']._serialized_start=2151 _globals['_ADSBIDINTENTACCEPTMESSAGE']._serialized_start=2094
_globals['_ADSBIDINTENTACCEPTMESSAGE']._serialized_end=2263 _globals['_ADSBIDINTENTACCEPTMESSAGE']._serialized_end=2206
# @@protoc_insertion_point(module_scope) # @@protoc_insertion_point(module_scope)

@ -142,6 +142,9 @@ def parseOfferFormData(swap_client, form_data, page_data, options={}):
parsed_data['rate'] = ci_from.make_int(parsed_data['amt_to'] / parsed_data['amt_from'], r=1) parsed_data['rate'] = ci_from.make_int(parsed_data['amt_to'] / parsed_data['amt_from'], r=1)
page_data['rate'] = ci_to.format_amount(parsed_data['rate']) page_data['rate'] = ci_to.format_amount(parsed_data['rate'])
if 'amt_to' not in parsed_data and 'rate' in parsed_data and 'amt_from' in parsed_data:
parsed_data['amt_to'] = int((parsed_data['amt_from'] * parsed_data['rate']) // ci_from.COIN())
page_data['amt_var'] = True if have_data_entry(form_data, 'amt_var') else False page_data['amt_var'] = True if have_data_entry(form_data, 'amt_var') else False
parsed_data['amt_var'] = page_data['amt_var'] parsed_data['amt_var'] = page_data['amt_var']
page_data['rate_var'] = True if have_data_entry(form_data, 'rate_var') else False page_data['rate_var'] = True if have_data_entry(form_data, 'rate_var') else False
@ -308,6 +311,8 @@ def postNewOfferFromParsed(swap_client, parsed_data):
extra_options['automation_id'] = parsed_data['automation_strat_id'] extra_options['automation_id'] = parsed_data['automation_strat_id']
swap_value = parsed_data['amt_from'] swap_value = parsed_data['amt_from']
if parsed_data.get('amt_to', None) is not None:
extra_options['amount_to'] = parsed_data['amt_to']
if parsed_data.get('subfee', False): if parsed_data.get('subfee', False):
ci_from = swap_client.ci(parsed_data['coin_from']) ci_from = swap_client.ci(parsed_data['coin_from'])
pi = swap_client.pi(swap_type) pi = swap_client.pi(swap_type)
@ -358,7 +363,7 @@ def offer_to_post_string(self, swap_client, offer_id):
'amt_from': ci_from.format_amount(offer.amount_from), 'amt_from': ci_from.format_amount(offer.amount_from),
'amt_bid_min': ci_from.format_amount(offer.min_bid_amount), 'amt_bid_min': ci_from.format_amount(offer.min_bid_amount),
'rate': ci_to.format_amount(offer.rate), 'rate': ci_to.format_amount(offer.rate),
'amt_to': ci_to.format_amount((offer.amount_from * offer.rate) // ci_from.COIN()), 'amt_to': ci_to.format_amount(offer.amount_to),
'validhrs': offer.time_valid // (60 * 60), 'validhrs': offer.time_valid // (60 * 60),
'swap_type': strSwapType(offer.swap_type), 'swap_type': strSwapType(offer.swap_type),
} }
@ -579,7 +584,7 @@ def page_offer(self, url_split, post_string):
'coin_from_ind': int(ci_from.coin_type()), 'coin_from_ind': int(ci_from.coin_type()),
'coin_to_ind': int(ci_to.coin_type()), 'coin_to_ind': int(ci_to.coin_type()),
'amt_from': ci_from.format_amount(offer.amount_from), 'amt_from': ci_from.format_amount(offer.amount_from),
'amt_to': ci_to.format_amount((offer.amount_from * offer.rate) // ci_from.COIN()), 'amt_to': ci_to.format_amount(offer.amount_to),
'amt_bid_min': ci_from.format_amount(offer.min_bid_amount), 'amt_bid_min': ci_from.format_amount(offer.min_bid_amount),
'rate': ci_to.format_amount(offer.rate), 'rate': ci_to.format_amount(offer.rate),
'lock_type': getLockName(offer.lock_type), 'lock_type': getLockName(offer.lock_type),
@ -781,7 +786,7 @@ def page_offers(self, url_split, post_string, sent=False):
ci_from.coin_name(), ci_from.coin_name(),
ci_to.coin_name(), ci_to.coin_name(),
ci_from.format_amount(o.amount_from), ci_from.format_amount(o.amount_from),
ci_to.format_amount((o.amount_from * o.rate) // ci_from.COIN()), ci_to.format_amount(o.amount_to),
ci_to.format_amount(o.rate), ci_to.format_amount(o.rate),
'Public' if o.addr_to == swap_client.network_addr else o.addr_to, 'Public' if o.addr_to == swap_client.network_addr else o.addr_to,
o.addr_from, o.addr_from,

@ -162,12 +162,14 @@ def describeBid(swap_client, bid, xmr_swap, offer, xmr_offer, bid_events, edit_b
ci_follower = ci_from if reverse_bid else ci_to ci_follower = ci_from if reverse_bid else ci_to
bid_amount: int = bid.amount bid_amount: int = bid.amount
bid_amount_to: int = bid.amount_to
bid_rate: int = offer.rate if bid.rate is None else bid.rate bid_rate: int = offer.rate if bid.rate is None else bid.rate
initiator_role: str = 'offerer' # Leader initiator_role: str = 'offerer' # Leader
participant_role: str = 'bidder' # Follower participant_role: str = 'bidder' # Follower
if reverse_bid: if reverse_bid:
bid_amount = bid.amount_to bid_amount = bid.amount_to
bid_amount_to = bid.amount
bid_rate = ci_from.make_int(bid.amount / bid.amount_to, r=1) bid_rate = ci_from.make_int(bid.amount / bid.amount_to, r=1)
initiator_role = 'bidder' initiator_role = 'bidder'
participant_role = 'offerer' participant_role = 'offerer'
@ -259,7 +261,7 @@ def describeBid(swap_client, bid, xmr_swap, offer, xmr_offer, bid_events, edit_b
'coin_from': ci_from.coin_name(), 'coin_from': ci_from.coin_name(),
'coin_to': ci_to.coin_name(), 'coin_to': ci_to.coin_name(),
'amt_from': ci_from.format_amount(bid_amount), 'amt_from': ci_from.format_amount(bid_amount),
'amt_to': ci_to.format_amount((bid_amount * bid_rate) // ci_from.COIN()), 'amt_to': ci_to.format_amount(bid_amount_to),
'bid_rate': ci_to.format_amount(bid_rate), 'bid_rate': ci_to.format_amount(bid_rate),
'ticker_from': ci_from.ticker(), 'ticker_from': ci_from.ticker(),
'ticker_to': ci_to.ticker(), 'ticker_to': ci_to.ticker(),

@ -1,3 +1,13 @@
0.13.0
==============
- GUI v3.0
- Bid and offer states change when expired.
- bid amounts are specified directly and not constructed from rate.
- Breaks compatibility with prior versions.
0.12.7 0.12.7
============== ==============

@ -185,7 +185,7 @@ class TestFunctions(BaseTest):
bid0 = read_json_api(1800 + id_offerer, f'bids/{bid_id.hex()}') bid0 = read_json_api(1800 + id_offerer, f'bids/{bid_id.hex()}')
bid1 = read_json_api(1800 + id_bidder, f'bids/{bid_id.hex()}') bid1 = read_json_api(1800 + id_bidder, f'bids/{bid_id.hex()}')
tolerance = 20 if reverse_bid else 0 tolerance = 1
assert (bid0['ticker_from'] == ci_from.ticker()) assert (bid0['ticker_from'] == ci_from.ticker())
assert (bid1['ticker_from'] == ci_from.ticker()) assert (bid1['ticker_from'] == ci_from.ticker())
assert (bid0['ticker_to'] == ci_to.ticker()) assert (bid0['ticker_to'] == ci_to.ticker())

@ -6,6 +6,7 @@
# file LICENSE or http://www.opensource.org/licenses/mit-license.php. # file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import hashlib import hashlib
import random
import secrets import secrets
import unittest import unittest
@ -41,7 +42,7 @@ from basicswap.util import (
from basicswap.messages_pb2 import ( from basicswap.messages_pb2 import (
BidMessage, BidMessage,
BidMessage_v1Deprecated, BidMessage_test,
) )
from basicswap.contrib.test_framework.script import hash160 as hash160_btc from basicswap.contrib.test_framework.script import hash160 as hash160_btc
@ -310,6 +311,89 @@ class Test(unittest.TestCase):
amount_to_recreate = int((amount_from * rate) // (10 ** scale_from)) amount_to_recreate = int((amount_from * rate) // (10 ** scale_from))
assert ('10.00000000' == format_amount(amount_to_recreate, scale_to)) assert ('10.00000000' == format_amount(amount_to_recreate, scale_to))
coin_settings = {'rpcport': 0, 'rpcauth': 'none', 'walletrpcport': 0, 'walletrpcauth': 'none'}
coin_settings.update(self.REQUIRED_SETTINGS)
ci_xmr = XMRInterface(coin_settings, 'regtest')
ci_btc = BTCInterface(coin_settings, 'regtest')
for i in range(10000):
test_pairs = random.randint(0, 3)
if test_pairs == 0:
ci_from = ci_btc
ci_to = ci_xmr
elif test_pairs == 1:
ci_from = ci_xmr
ci_to = ci_btc
elif test_pairs == 2:
ci_from = ci_xmr
ci_to = ci_xmr
else:
ci_from = ci_btc
ci_to = ci_btc
test_range = random.randint(0, 5)
if test_range == 0:
amount_from = random.randint(10000, 1 * ci_from.COIN())
elif test_range == 1:
amount_from = random.randint(10000, 1000 * ci_from.COIN())
elif test_range == 2:
amount_from = random.randint(10000, 2100 * ci_from.COIN())
elif test_range == 3:
amount_from = random.randint(10000, 210000 * ci_from.COIN())
elif test_range == 4:
amount_from = random.randint(10000, 21000000 * ci_from.COIN())
else:
amount_from = random.randint(10000, 2100000000 * ci_from.COIN())
test_range = random.randint(0, 5)
if test_range == 0:
amount_to = random.randint(10000, 1 * ci_to.COIN())
elif test_range == 1:
amount_to = random.randint(10000, 1000 * ci_to.COIN())
elif test_range == 2:
amount_to = random.randint(10000, 2100 * ci_to.COIN())
elif test_range == 3:
amount_to = random.randint(10000, 210000 * ci_to.COIN())
elif test_range == 4:
amount_to = random.randint(10000, 21000000 * ci_to.COIN())
else:
amount_to = random.randint(10000, 2100000000 * ci_to.COIN())
offer_rate = ci_from.make_int(amount_to / amount_from, r=1)
amount_to_from_rate: int = int((int(amount_from) * offer_rate) // (10 ** scale_from))
scale_from = 24
offer_rate = make_int(amount_to, scale_from) // amount_from
amount_to_from_rate: int = int((int(amount_from) * offer_rate) // (10 ** scale_from))
if abs(amount_to - amount_to_from_rate) == 1:
offer_rate += 1
offer_rate_human_read: int = int(offer_rate // (10 ** (scale_from - ci_from.exp())))
amount_to_from_rate: int = int((int(amount_from) * offer_rate) // (10 ** scale_from))
if amount_to != amount_to_from_rate:
print('from exp, amount', ci_from.exp(), amount_from)
print('to exp, amount', ci_to.exp(), amount_to)
print('amount_to_from_rate', amount_to_from_rate)
raise ValueError('Bad amount_to')
scale_to = 24
reversed_rate = make_int(amount_from, scale_to) // amount_to
amount_from_from_rate: int = int((int(amount_to) * reversed_rate) // (10 ** scale_to))
if abs(amount_from - amount_from_from_rate) == 1:
reversed_rate += 1
amount_from_from_rate: int = int((int(amount_to) * reversed_rate) // (10 ** scale_to))
if amount_from != amount_from_from_rate:
print('from exp, amount', ci_from.exp(), amount_from)
print('to exp, amount', ci_to.exp(), amount_to)
print('amount_from_from_rate', amount_from_from_rate)
raise ValueError('Bad amount_from')
def test_rfc2440(self): def test_rfc2440(self):
password = 'test' password = 'test'
salt = bytes.fromhex('B7A94A7E4988630E') salt = bytes.fromhex('B7A94A7E4988630E')
@ -330,14 +414,20 @@ class Test(unittest.TestCase):
def test_protobuf(self): def test_protobuf(self):
# Ensure old protobuf templates can be read # Ensure old protobuf templates can be read
msg_buf = BidMessage_v1Deprecated() msg_buf = BidMessage_test()
msg_buf.protocol_version = 2 msg_buf.protocol_version = 2
msg_buf.time_valid = 1024
serialised_msg = msg_buf.SerializeToString() serialised_msg = msg_buf.SerializeToString()
msg_buf_v2 = BidMessage() msg_buf_v2 = BidMessage()
msg_buf_v2.ParseFromString(serialised_msg) msg_buf_v2.ParseFromString(serialised_msg)
assert (msg_buf_v2.protocol_version == 2)
assert (msg_buf_v2.time_valid == 1024)
# Decode only the first field
msg_buf_v2.ParseFromString(serialised_msg[:2])
assert (msg_buf_v2.protocol_version == 2) assert (msg_buf_v2.protocol_version == 2)
assert (msg_buf_v2.time_valid == 0)
def test_is_private_ip_address(self): def test_is_private_ip_address(self):
assert (is_private_ip_address('localhost')) assert (is_private_ip_address('localhost'))

Loading…
Cancel
Save