diff --git a/Dockerfile b/Dockerfile
index d51f42f..c121e92 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -15,9 +15,10 @@ RUN wget -O protobuf_src.tar.gz https://github.com/protocolbuffers/protobuf/rele
make install && \
ldconfig
-RUN wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/refs/tags/anonswap_v0.1.zip && \
- unzip -d coincurve-anonswap coincurve-anonswap.zip && \
- mv ./coincurve-anonswap/*/{.,}* ./coincurve-anonswap || true && \
+ARG COINCURVE_VERSION=v0.1
+RUN wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/refs/tags/anonswap_$COINCURVE_VERSION.zip && \
+ unzip coincurve-anonswap.zip && \
+ mv ./coincurve-anonswap_$COINCURVE_VERSION ./coincurve-anonswap && \
cd coincurve-anonswap && \
python3 setup.py install --force
diff --git a/basicswap/__init__.py b/basicswap/__init__.py
index 7b60633..440097b 100644
--- a/basicswap/__init__.py
+++ b/basicswap/__init__.py
@@ -1,3 +1,3 @@
name = "basicswap"
-__version__ = "0.0.33"
+__version__ = "0.0.34"
diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py
index 341fdfb..8a71404 100644
--- a/basicswap/basicswap.py
+++ b/basicswap/basicswap.py
@@ -225,6 +225,8 @@ class BasicSwap(BaseApp):
# TODO: Adjust ranges
self.min_delay_event = self.settings.get('min_delay_event', 10)
self.max_delay_event = self.settings.get('max_delay_event', 60)
+ self.min_delay_event_short = self.settings.get('min_delay_event_short', 2)
+ self.max_delay_event_short = self.settings.get('max_delay_event_short', 30)
self.min_delay_retry = self.settings.get('min_delay_retry', 60)
self.max_delay_retry = self.settings.get('max_delay_retry', 5 * 60)
@@ -893,6 +895,21 @@ class BasicSwap(BaseApp):
nm += 1
self.log.info('Scanned %d unread messages.', nm)
+ def getActiveBidMsgValidTime(self):
+ return self.SMSG_SECONDS_IN_HOUR * 48
+
+ def getAcceptBidMsgValidTime(self, bid):
+ now = int(time.time())
+ smsg_max_valid = self.SMSG_SECONDS_IN_HOUR * 48
+ smsg_min_valid = self.SMSG_SECONDS_IN_HOUR * 1
+ bid_valid = (bid.expire_at - now) + 10 * 60 # Add 10 minute buffer
+ return max(smsg_min_valid, min(smsg_max_valid, bid_valid))
+
+ def sendSmsg(self, addr_from, addr_to, payload_hex, msg_valid):
+ options = {'decodehex': True, 'ttl_is_seconds': True}
+ ro = self.callrpc('smsgsend', [addr_from, addr_to, payload_hex, False, msg_valid, False, options])
+ return bytes.fromhex(ro['msgid'])
+
def validateSwapType(self, coin_from, coin_to, swap_type):
if coin_from == Coins.XMR:
raise ValueError('TODO: XMR coin_from')
@@ -1054,13 +1071,8 @@ class BasicSwap(BaseApp):
offer_bytes = msg_buf.SerializeToString()
payload_hex = str.format('{:02x}', MessageTypes.OFFER) + offer_bytes.hex()
-
- options = {'decodehex': True, 'ttl_is_seconds': True}
msg_valid = max(self.SMSG_SECONDS_IN_HOUR * 1, valid_for_seconds)
- ro = self.callrpc('smsgsend', [offer_addr, offer_addr_to, payload_hex, False, msg_valid, False, options])
- msg_id = ro['msgid']
-
- offer_id = bytes.fromhex(msg_id)
+ offer_id = self.sendSmsg(offer_addr, offer_addr_to, payload_hex, msg_valid)
security_token = extra_options.get('security_token', None)
if security_token is not None and len(security_token) != 20:
@@ -1145,10 +1157,8 @@ class BasicSwap(BaseApp):
msg_bytes = msg_buf.SerializeToString()
payload_hex = str.format('{:02x}', MessageTypes.OFFER_REVOKE) + msg_bytes.hex()
-
- options = {'decodehex': True, 'ttl_is_seconds': True}
- ro = self.callrpc('smsgsend', [offer.addr_from, self.network_addr, payload_hex, False, offer.time_valid, False, options])
- msg_id = ro['msgid']
+ msg_id = self.sendSmsg(offer.addr_from, self.network_addr, payload_hex, offer.time_valid)
+ self.log.debug('Revoked offer %s in msg %s', offer_id.hex(), msg_id.hex())
finally:
if session:
session.close()
@@ -1680,10 +1690,8 @@ class BasicSwap(BaseApp):
bid_addr = self.newSMSGAddress(use_type=AddressTypes.BID)[0] if addr_send_from is None else addr_send_from
options = {'decodehex': True, 'ttl_is_seconds': True}
msg_valid = max(self.SMSG_SECONDS_IN_HOUR * 1, valid_for_seconds)
- ro = self.callrpc('smsgsend', [bid_addr, offer.addr_from, payload_hex, False, msg_valid, False, options])
- msg_id = ro['msgid']
- bid_id = bytes.fromhex(msg_id)
+ bid_id = self.sendSmsg(bid_addr, offer.addr_from, payload_hex, msg_valid)
bid = Bid(
protocol_version=msg_buf.protocol_version,
active_ind=1,
@@ -1962,19 +1970,13 @@ class BasicSwap(BaseApp):
bid_bytes = msg_buf.SerializeToString()
payload_hex = str.format('{:02x}', MessageTypes.BID_ACCEPT) + bid_bytes.hex()
- options = {'decodehex': True, 'ttl_is_seconds': True}
- # TODO: set msg_valid based on bid / offer parameters
- msg_valid = self.SMSG_SECONDS_IN_HOUR * 48
- ro = self.callrpc('smsgsend', [offer.addr_from, bid.bid_addr, payload_hex, False, msg_valid, False, options])
- msg_id = ro['msgid']
- accept_msg_id = bytes.fromhex(msg_id)
+ msg_valid = self.getAcceptBidMsgValidTime(bid)
+ bid.accept_msg_id = self.sendSmsg(offer.addr_from, bid.bid_addr, payload_hex, msg_valid)
- bid.accept_msg_id = accept_msg_id
+ self.log.info('Sent BID_ACCEPT %s', bid.accept_msg_id.hex())
bid.setState(BidStates.BID_ACCEPTED)
- self.log.info('Sent BID_ACCEPT %s', accept_msg_id.hex())
-
self.saveBid(bid_id, bid)
self.swaps_in_progress[bid_id] = (bid, offer)
@@ -2064,8 +2066,7 @@ class BasicSwap(BaseApp):
bid_addr = self.newSMSGAddress(use_type=AddressTypes.BID)[0] if addr_send_from is None else addr_send_from
options = {'decodehex': True, 'ttl_is_seconds': True}
msg_valid = max(self.SMSG_SECONDS_IN_HOUR * 1, valid_for_seconds)
- ro = self.callrpc('smsgsend', [bid_addr, offer.addr_from, payload_hex, False, msg_valid, False, options])
- xmr_swap.bid_id = bytes.fromhex(ro['msgid'])
+ xmr_swap.bid_id = self.sendSmsg(bid_addr, offer.addr_from, payload_hex, msg_valid)
if coin_to == Coins.XMR:
msg_buf2 = XmrSplitMessage(
@@ -2076,8 +2077,7 @@ class BasicSwap(BaseApp):
)
msg_bytes = msg_buf2.SerializeToString()
payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_SPLIT) + msg_bytes.hex()
- ro = self.callrpc('smsgsend', [bid_addr, offer.addr_from, payload_hex, False, msg_valid, False, options])
- xmr_swap.bid_msg_id2 = bytes.fromhex(ro['msgid'])
+ xmr_swap.bid_msg_id2 = self.sendSmsg(bid_addr, offer.addr_from, payload_hex, msg_valid)
msg_buf3 = XmrSplitMessage(
msg_id=xmr_swap.bid_id,
@@ -2087,8 +2087,7 @@ class BasicSwap(BaseApp):
)
msg_bytes = msg_buf3.SerializeToString()
payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_SPLIT) + msg_bytes.hex()
- 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'])
+ xmr_swap.bid_msg_id3 = self.sendSmsg(bid_addr, offer.addr_from, payload_hex, msg_valid)
bid = Bid(
protocol_version=msg_buf.protocol_version,
@@ -2266,11 +2265,9 @@ class BasicSwap(BaseApp):
msg_bytes = msg_buf.SerializeToString()
payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_ACCEPT_LF) + msg_bytes.hex()
- options = {'decodehex': True, 'ttl_is_seconds': True}
- msg_valid = self.SMSG_SECONDS_IN_HOUR * 48
- ro = self.callrpc('smsgsend', [offer.addr_from, bid.bid_addr, payload_hex, False, msg_valid, False, options])
- msg_id = ro['msgid']
- bid.accept_msg_id = bytes.fromhex(msg_id)
+
+ msg_valid = self.getAcceptBidMsgValidTime(bid)
+ bid.accept_msg_id = self.sendSmsg(offer.addr_from, bid.bid_addr, payload_hex, msg_valid)
xmr_swap.bid_accept_msg_id = bid.accept_msg_id
if coin_to == Coins.XMR:
@@ -2282,8 +2279,7 @@ class BasicSwap(BaseApp):
)
msg_bytes = msg_buf2.SerializeToString()
payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_SPLIT) + msg_bytes.hex()
- ro = self.callrpc('smsgsend', [offer.addr_from, bid.bid_addr, payload_hex, False, msg_valid, False, options])
- xmr_swap.bid_accept_msg_id2 = bytes.fromhex(ro['msgid'])
+ xmr_swap.bid_accept_msg_id2 = self.sendSmsg(offer.addr_from, bid.bid_addr, payload_hex, msg_valid)
msg_buf3 = XmrSplitMessage(
msg_id=bid_id,
@@ -2293,8 +2289,7 @@ class BasicSwap(BaseApp):
)
msg_bytes = msg_buf3.SerializeToString()
payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_SPLIT) + msg_bytes.hex()
- ro = self.callrpc('smsgsend', [offer.addr_from, bid.bid_addr, payload_hex, False, msg_valid, False, options])
- xmr_swap.bid_accept_msg_id3 = bytes.fromhex(ro['msgid'])
+ xmr_swap.bid_accept_msg_id3 = self.sendSmsg(offer.addr_from, bid.bid_addr, payload_hex, msg_valid)
bid.setState(BidStates.BID_ACCEPTED)
@@ -2946,7 +2941,7 @@ class BasicSwap(BaseApp):
rv = True # Remove from swaps_in_progress
elif state == BidStates.XMR_SWAP_FAILED_SWIPED:
rv = True # Remove from swaps_in_progress
- elif state == BidStates.XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX:
+ elif state in (BidStates.XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX, BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX):
if bid.xmr_a_lock_tx is None:
return rv
@@ -3585,6 +3580,8 @@ class BasicSwap(BaseApp):
self.redeemXmrBidCoinBLockTx(row.linked_id, session)
elif row.action_type == ActionTypes.RECOVER_XMR_SWAP_LOCK_TX_B:
self.recoverXmrBidCoinBLockTx(row.linked_id, session)
+ elif row.action_type == ActionTypes.SEND_XMR_SWAP_LOCK_SPEND_MSG:
+ self.sendXmrBidCoinALockSpendTxMsg(row.linked_id, session)
else:
self.log.warning('Unknown event type: %d', row.event_type)
except Exception as ex:
@@ -4165,7 +4162,7 @@ class BasicSwap(BaseApp):
if xmr_swap.pkal == xmr_swap.pkaf:
raise ValueError('Duplicate script spend pubkey.')
- bid.setState(BidStates.SWAP_DELAYING)
+ bid.setState(BidStates.BID_ACCEPTED) # XMR
self.saveBidInSession(bid.bid_id, bid, session, xmr_swap)
delay = random.randrange(self.min_delay_event, self.max_delay_event)
@@ -4387,11 +4384,8 @@ class BasicSwap(BaseApp):
msg_bytes = msg_buf.SerializeToString()
payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_TXN_SIGS_FL) + msg_bytes.hex()
- options = {'decodehex': True, 'ttl_is_seconds': True}
- # TODO: set msg_valid based on bid / offer parameters
- msg_valid = self.SMSG_SECONDS_IN_HOUR * 48
- ro = self.callrpc('smsgsend', [bid.bid_addr, offer.addr_from, payload_hex, False, msg_valid, False, options])
- xmr_swap.coin_a_lock_tx_sigs_l_msg_id = bytes.fromhex(ro['msgid'])
+ msg_valid = self.getActiveBidMsgValidTime()
+ xmr_swap.coin_a_lock_tx_sigs_l_msg_id = self.sendSmsg(bid.bid_addr, offer.addr_from, payload_hex, msg_valid)
self.log.info('Sent XMR_BID_TXN_SIGS_FL %s', xmr_swap.coin_a_lock_tx_sigs_l_msg_id.hex())
@@ -4406,7 +4400,7 @@ class BasicSwap(BaseApp):
)
bid.xmr_a_lock_tx.setState(TxStates.TX_NONE)
- bid.setState(BidStates.BID_ACCEPTED) # XMR
+ bid.setState(BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_TX_SIGS)
self.watchXmrSwap(bid, offer, xmr_swap)
self.saveBidInSession(bid_id, bid, session, xmr_swap)
except Exception as ex:
@@ -4414,7 +4408,7 @@ class BasicSwap(BaseApp):
self.log.error(traceback.format_exc())
def sendXmrBidCoinALockTx(self, bid_id, session):
- # Send coin A lock tx and MSG4F L -> F
+ # Offerer/Leader. Send coin A lock tx
self.log.debug('Sending coin A lock tx for xmr bid %s', bid_id.hex())
bid, xmr_swap = self.getXmrBidFromSession(session, bid_id)
@@ -4431,6 +4425,10 @@ class BasicSwap(BaseApp):
kal = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, KeyTypes.KAL)
+ # Prove leader can sign for kal, sent in MSG4F
+ xmr_swap.kal_sig = ci_from.signCompact(kal, 'proof key owned for swap')
+
+ # Create Script lock spend tx
xmr_swap.a_lock_spend_tx = ci_from.createScriptLockSpendTx(
xmr_swap.a_lock_tx, xmr_swap.a_lock_tx_script,
xmr_swap.dest_af,
@@ -4440,24 +4438,9 @@ class BasicSwap(BaseApp):
prevout_amount = ci_from.getLockTxSwapOutputValue(bid, xmr_swap)
xmr_swap.al_lock_spend_tx_esig = ci_from.signTxOtVES(kal, xmr_swap.pkasf, xmr_swap.a_lock_spend_tx, 0, xmr_swap.a_lock_tx_script, prevout_amount)
- # Prove leader can sign for kal
- xmr_swap.kal_sig = ci_from.signCompact(kal, 'proof key owned for swap')
-
- msg_buf = XmrBidLockSpendTxMessage(
- bid_msg_id=bid_id,
- a_lock_spend_tx=xmr_swap.a_lock_spend_tx,
- kal_sig=xmr_swap.kal_sig)
-
- msg_bytes = msg_buf.SerializeToString()
- payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_LOCK_SPEND_TX_LF) + msg_bytes.hex()
-
- options = {'decodehex': True, 'ttl_is_seconds': True}
- # TODO: set msg_valid based on bid / offer parameters
- msg_valid = self.SMSG_SECONDS_IN_HOUR * 48
- ro = self.callrpc('smsgsend', [offer.addr_from, bid.bid_addr, payload_hex, False, msg_valid, False, options])
- xmr_swap.coin_a_lock_refund_spend_tx_msg_id = bytes.fromhex(ro['msgid'])
-
- # TODO: Separate MSG4F and txn sending
+ delay = random.randrange(self.min_delay_event_short, self.max_delay_event_short)
+ self.log.info('Sending lock spend tx message for bid %s in %d seconds', bid_id.hex(), delay)
+ self.createActionInSession(delay, ActionTypes.SEND_XMR_SWAP_LOCK_SPEND_MSG, bid_id, session)
# publishalocktx
lock_tx_signed = ci_from.signTxWithWallet(xmr_swap.a_lock_tx)
@@ -4560,11 +4543,8 @@ class BasicSwap(BaseApp):
msg_bytes = msg_buf.SerializeToString()
payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_LOCK_RELEASE_LF) + msg_bytes.hex()
- options = {'decodehex': True, 'ttl_is_seconds': True}
- # TODO: set msg_valid based on bid / offer parameters
- msg_valid = self.SMSG_SECONDS_IN_HOUR * 48
- ro = self.callrpc('smsgsend', [offer.addr_from, bid.bid_addr, payload_hex, False, msg_valid, False, options])
- xmr_swap.coin_a_lock_refund_spend_tx_msg_id = bytes.fromhex(ro['msgid'])
+ msg_valid = self.getActiveBidMsgValidTime()
+ xmr_swap.coin_a_lock_release_msg_id = self.sendSmsg(offer.addr_from, bid.bid_addr, payload_hex, msg_valid)
bid.setState(BidStates.XMR_SWAP_LOCK_RELEASED)
self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer)
@@ -4740,6 +4720,33 @@ class BasicSwap(BaseApp):
bid.setState(BidStates.XMR_SWAP_NOSCRIPT_TX_RECOVERED)
self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer)
+ def sendXmrBidCoinALockSpendTxMsg(self, bid_id, session):
+ # Send MSG4F L -> F
+ self.log.debug('Sending coin A lock spend tx msg for xmr bid %s', bid_id.hex())
+
+ bid, xmr_swap = self.getXmrBidFromSession(session, bid_id)
+ ensure(bid, 'Bid not found: {}.'.format(bid_id.hex()))
+ ensure(xmr_swap, 'XMR swap not found: {}.'.format(bid_id.hex()))
+
+ offer, xmr_offer = self.getXmrOfferFromSession(session, bid.offer_id, sent=False)
+ ensure(offer, 'Offer not found: {}.'.format(bid.offer_id.hex()))
+ ensure(xmr_offer, 'XMR offer not found: {}.'.format(bid.offer_id.hex()))
+ ci_from = self.ci(offer.coin_from)
+
+ msg_buf = XmrBidLockSpendTxMessage(
+ bid_msg_id=bid_id,
+ a_lock_spend_tx=xmr_swap.a_lock_spend_tx,
+ kal_sig=xmr_swap.kal_sig)
+
+ msg_bytes = msg_buf.SerializeToString()
+ payload_hex = str.format('{:02x}', MessageTypes.XMR_BID_LOCK_SPEND_TX_LF) + msg_bytes.hex()
+
+ msg_valid = self.getActiveBidMsgValidTime()
+ xmr_swap.coin_a_lock_refund_spend_tx_msg_id = self.sendSmsg(offer.addr_from, bid.bid_addr, payload_hex, msg_valid)
+
+ bid.setState(BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX)
+ self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer)
+
def processXmrBidCoinALockSigs(self, msg):
# Leader processing MSG3L
self.log.debug('Processing xmr coin a follower lock sigs msg %s', msg['msgid'])
@@ -4795,7 +4802,7 @@ class BasicSwap(BaseApp):
self.log.info('Sending coin A lock tx for xmr bid %s in %d seconds', bid_id.hex(), delay)
self.createAction(delay, ActionTypes.SEND_XMR_SWAP_LOCK_TX_A, bid_id)
- bid.setState(BidStates.SWAP_DELAYING)
+ bid.setState(BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_TX_SIGS)
self.saveBid(bid_id, bid, xmr_swap=xmr_swap)
except Exception as ex:
if self.debug:
diff --git a/basicswap/basicswap_util.py b/basicswap/basicswap_util.py
index 3ce9935..7854e8c 100644
--- a/basicswap/basicswap_util.py
+++ b/basicswap/basicswap_util.py
@@ -96,6 +96,8 @@ class BidStates(IntEnum):
BID_STALLED_FOR_TEST = 24
BID_REJECTED = 25
BID_STATE_UNKNOWN = 26
+ XMR_SWAP_MSG_SCRIPT_LOCK_TX_SIGS = 27 # XmrBidLockTxSigsMessage
+ XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX = 28 # XmrBidLockSpendTxMessage
class TxStates(IntEnum):
@@ -132,6 +134,7 @@ class ActionTypes(IntEnum):
REDEEM_XMR_SWAP_LOCK_TX_A = auto() # Follower
REDEEM_XMR_SWAP_LOCK_TX_B = auto() # Leader
RECOVER_XMR_SWAP_LOCK_TX_B = auto()
+ SEND_XMR_SWAP_LOCK_SPEND_MSG = auto()
class EventLogTypes(IntEnum):
@@ -235,6 +238,10 @@ def strBidState(state):
return 'Failed'
if state == BidStates.SWAP_DELAYING:
return 'Delaying'
+ if state == BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_TX_SIGS:
+ return 'Exchanged script lock tx sigs msg'
+ if state == BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX:
+ return 'Exchanged script lock spend tx msg'
return 'Unknown' + ' ' + str(state)
@@ -408,4 +415,8 @@ def isActiveBidState(state):
return True
if state == BidStates.XMR_SWAP_SCRIPT_TX_PREREFUND:
return True
+ if state == BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_TX_SIGS:
+ return True
+ if state == BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX:
+ return True
return False
diff --git a/basicswap/db.py b/basicswap/db.py
index 75b036c..e5bf6f7 100644
--- a/basicswap/db.py
+++ b/basicswap/db.py
@@ -12,8 +12,8 @@ from enum import IntEnum, auto
from sqlalchemy.ext.declarative import declarative_base
-CURRENT_DB_VERSION = 14
-CURRENT_DB_DATA_VERSION = 1
+CURRENT_DB_VERSION = 15
+CURRENT_DB_DATA_VERSION = 2
Base = declarative_base()
@@ -301,7 +301,8 @@ class XmrSwap(Base):
bid_accept_msg_id3 = sa.Column(sa.LargeBinary)
coin_a_lock_tx_sigs_l_msg_id = sa.Column(sa.LargeBinary) # MSG3L F -> L
- coin_a_lock_refund_spend_tx_msg_id = sa.Column(sa.LargeBinary) # MSG4F L -> F
+ coin_a_lock_spend_tx_msg_id = sa.Column(sa.LargeBinary) # MSG4F L -> F
+ coin_a_lock_release_msg_id = sa.Column(sa.LargeBinary) # MSG5F L -> F
contract_count = sa.Column(sa.Integer)
diff --git a/basicswap/db_upgrades.py b/basicswap/db_upgrades.py
index 08f79c1..6e1784f 100644
--- a/basicswap/db_upgrades.py
+++ b/basicswap/db_upgrades.py
@@ -59,6 +59,15 @@ def upgradeDatabaseData(self, data_version):
label=strBidState(state),
created_at=now))
+ if data_version < 2:
+ for state in (BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_TX_SIGS, BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX):
+ session.add(BidState(
+ active_ind=1,
+ state_id=int(state),
+ in_progress=isActiveBidState(state),
+ label=strBidState(state),
+ created_at=now))
+
self.db_data_version = CURRENT_DB_DATA_VERSION
self.setIntKVInSession('db_data_version', self.db_data_version, session)
session.commit()
@@ -203,7 +212,10 @@ def upgradeDatabase(self, db_version):
session.execute('ALTER TABLE actions RENAME COLUMN event_id TO action_id')
session.execute('ALTER TABLE actions RENAME COLUMN event_type TO action_type')
session.execute('ALTER TABLE actions RENAME COLUMN event_data TO action_data')
-
+ elif current_version == 14:
+ db_version += 1
+ session.execute('ALTER TABLE xmr_swaps ADD COLUMN coin_a_lock_release_msg_id BLOB')
+ session.execute('ALTER TABLE xmr_swaps RENAME COLUMN coin_a_lock_refund_spend_tx_msg_id TO coin_a_lock_spend_tx_msg_id')
if current_version != db_version:
self.db_version = db_version
self.setIntKVInSession('db_version', db_version, session)
diff --git a/basicswap/static/sequence_diagrams/xmr.bidder.alt.xu.min.svg b/basicswap/static/sequence_diagrams/xmr.bidder.alt.xu.min.svg
index c4b0e55..ae54aed 100644
--- a/basicswap/static/sequence_diagrams/xmr.bidder.alt.xu.min.svg
+++ b/basicswap/static/sequence_diagrams/xmr.bidder.alt.xu.min.svg
@@ -1 +1,407 @@
-
\ No newline at end of file
+
diff --git a/basicswap/static/sequence_diagrams/xmr.offerer.alt.xu.min.svg b/basicswap/static/sequence_diagrams/xmr.offerer.alt.xu.min.svg
index 2b53bc2..7c9a847 100644
--- a/basicswap/static/sequence_diagrams/xmr.offerer.alt.xu.min.svg
+++ b/basicswap/static/sequence_diagrams/xmr.offerer.alt.xu.min.svg
@@ -1 +1,388 @@
-
\ No newline at end of file
+
diff --git a/doc/protocols/sequence_diagrams/.gitignore b/doc/protocols/sequence_diagrams/.gitignore
new file mode 100644
index 0000000..756b22f
--- /dev/null
+++ b/doc/protocols/sequence_diagrams/.gitignore
@@ -0,0 +1 @@
+*.svg
diff --git a/doc/protocols/sequence_diagrams/xmr.bidder.alt.xu b/doc/protocols/sequence_diagrams/xmr.bidder.alt.xu
new file mode 100644
index 0000000..f001172
--- /dev/null
+++ b/doc/protocols/sequence_diagrams/xmr.bidder.alt.xu
@@ -0,0 +1,80 @@
+xu {
+ hscale="1.3", wordwraparcs=on;
+
+ CB [label=" ", linecolor="transparent"],
+ N [label="Network", linecolor="#008800", textbgcolor="#CCFFCC", arclinecolor="#008800"],
+ O [label="Offerer", linecolor="#FF0000", textbgcolor="#FFCCCC", arclinecolor="#FF0000"],
+ B [label="Bidder", linecolor="#0000FF", textbgcolor="#CCCCFF", arclinecolor="#0000FF"],
+ C [label=" ", linecolor="transparent"], C2 [label=" ", linecolor="transparent"];
+ O =>> N [label="Sends Offer"];
+ N >> B [label="Detects Offer"];
+ B =>> O [label="Sends Bid"];
+ B abox B [label="Bid Sent"];
+ O box O [label="User accepts bid"];
+ O =>> B [label="Sends BidAccept message"],
+ C note C2
+ [label="The BidAccept message contains the pubkeys the offerer will use and a DLEAG proof one key will work across both chains of the swapping coins",
+ textbgcolor="#FFFFCC"];
+ B abox B [label="Bid Receiving accept"];
+ B abox B [label="Bid Accepted"];
+ B =>> O [label="Sends XmrBidLockTxSigsMessage"],
+ C note C2
+ [label="The XmrBidLockTxSigsMessage contains the bidder's signatures for the script-coin-lock-refund and script-coin-lock-refund-spend txns.",
+ textbgcolor="#FFFFCC"];
+ B abox B [label="Exchanged script lock tx sigs msg"];
+ O =>> B [label="Sends XmrBidLockSpendTxMessage"],
+ C note C2
+ [label="The XmrBidLockSpendTxMessage contains the script-coin-lock-tx and the offerer's signature for it.",
+ textbgcolor="#FFFFCC"];
+ O =>> N [label="Sends script-coin-lock-tx"],
+ B abox B [label="Bid Script coin spend tx valid"];
+ B => B [label="Wait for script-coin-lock-tx to confirm"];
+ B abox B [label="Bid Script coin locked"];
+ CB alt C [label="success path"] {
+ B =>> N [label="Sends noscript-coin-lock-tx"];
+ B => B [label="Wait for noscript-coin-lock-tx to confirm"], O => O [label="Wait for noscript-coin-lock-tx to confirm"];
+ B abox B [label="Bid Scriptless coin locked"];
+ O => B [label="Sends script-coin-lock-tx release message"],
+ C note C2
+ [label="The XmrBidLockReleaseMessage contains the offerer's OTVES for it.
+ The bidder decodes the offerer's signature from the OTVES.
+ When the offerer has the plaintext signature, they can decode the bidder's noscript-coin-lock-tx signature.",
+ textbgcolor="#FFFFCC"];
+ B abox B [label="Script coin lock released"];
+ B =>> N [label="Sends script-coin-lock-spend-tx"];
+ B abox B [label="Script tx redeemed"];
+ B abox B [label="Bid Completed"];
+ --- [label="fail path"];
+ |||;
+ B => B [label="Wait for script-coin-lock-tx lock to expire"];
+ O =>> N [label="Sends script-coin-lock-pre-refund-tx"],
+ C note C2
+ [label="tx can be sent by either party.",
+ textbgcolor="#FFFFCC"];
+ N >> O [label="script-coin-lock-pre-refund-tx"];
+ B abox B [label="Bid Script pre-refund tx in chain"];
+ CB alt C [label="offerer refunds script coin lock tx"] {
+ |||;
+ O => O [label="Wait for pre-refund tx to confirm"];
+ O =>> N [label="Sends script-coin-lock-pre-refund-spend-tx"],
+ C note C2
+ [label="Refunds the script lock tx, with the offerer's cleartext signature the bidder can refund the noscript lock tx.
+ Once the lock expires the pre-refund tx can be spent by the bidder.",
+ textbgcolor="#FFFFCC"];
+ O abox O [label="Bid Failed, refunded"];
+ N >> B [label="Detects script-coin-lock-pre-refund-spend-tx"],
+ C note C2
+ [label="Bidder recovers the offerer's scriptless chain key-shard.",
+ textbgcolor="#FFFFCC"];
+ B =>> N [label="Sends scriptless-coin-lock-recover-tx"];
+ B abox B [label="Bid Scriptless tx recovered"];
+ B abox B [label="Bid Failed, refunded"];
+ --- [label="bidder swipes script coin lock tx"];
+ |||;
+ B => B [label="Wait for pre-refund tx lock to expire"];
+ B =>> N [label="Sends script-coin-lock-pre-refund-swipe-tx"];
+ B abox B [label="Bid Failed, swiped"];
+ };
+ };
+}
+
diff --git a/doc/protocols/sequence_diagrams/xmr.offerer.alt.xu b/doc/protocols/sequence_diagrams/xmr.offerer.alt.xu
index 7b789d7..c48c53b 100644
--- a/doc/protocols/sequence_diagrams/xmr.offerer.alt.xu
+++ b/doc/protocols/sequence_diagrams/xmr.offerer.alt.xu
@@ -21,12 +21,15 @@ xu {
C note C2
[label="The XmrBidLockTxSigsMessage contains the bidder's signatures for the script-coin-lock-refund and script-coin-lock-refund-spend txns.",
textbgcolor="#FFFFCC"];
+ O abox O [label="Exchanged script lock tx sigs msg"];
+ O =>> N [label="Sends script-coin-lock-tx"];
+ O abox O [label="Bid Script coin spend tx valid"];
O =>> B [label="Sends XmrBidLockSpendTxMessage"],
C note C2
[label="The XmrBidLockSpendTxMessage contains the script-coin-lock-tx and the offerer's signature for it.",
textbgcolor="#FFFFCC"];
- O =>> N [label="Sends script-coin-lock-tx"],
- O abox O [label="Bid Script coin spend tx valid"];
+ O abox O [label="Exchanged script lock spend tx msg"];
+
|||;
B => B [label="Wait for script-coin-lock-tx to confirm"], O => O [label="Wait for script-coin-lock-tx to confirm"];
O abox O [label="Bid Script coin locked"];
diff --git a/doc/protocols/sequence_diagrams/xmr.offerer.simple.xu b/doc/protocols/sequence_diagrams/xmr.offerer.simple.xu
deleted file mode 100644
index dcce045..0000000
--- a/doc/protocols/sequence_diagrams/xmr.offerer.simple.xu
+++ /dev/null
@@ -1,55 +0,0 @@
-xu {
- hscale="1.3", wordwraparcs=on;
-
- CB [label=" ", linecolor="transparent"],
- N [label="Network", linecolor="#008800", textbgcolor="#CCFFCC", arclinecolor="#008800"],
- O [label="Offerer", linecolor="#FF0000", textbgcolor="#FFCCCC", arclinecolor="#FF0000"],
- B [label="Bidder", linecolor="#0000FF", textbgcolor="#CCCCFF", arclinecolor="#0000FF"],
- C [label=" ", linecolor="transparent"], C2 [label=" ", linecolor="transparent"];
- O =>> N [label="Sends Offer"];
- N >> B [label="Detects Offer"];
- B =>> O [label="Sends Bid"];
- O abox O [label="Bid Receiving"];
- O abox O [label="Bid Received"];
- O box O [label="Input: Accepts bid"];
- O =>> B [label="Sends BidAccept message"],
- C note C2
- [label="The BidAccept message contains the pubkeys the offerer will use and a DLEAG proof one key will work across both chains of the swapping coins",
- textbgcolor="#FFFFCC"];
- O abox O [label="Bid Accepted"];
- B =>> O [label="Sends XmrBidLockTxSigsMessage"],
- C note C2
- [label="The XmrBidLockTxSigsMessage contains the bidder's signatures for the script-coin-lock-refund and script-coin-lock-refund-spend txns.",
- textbgcolor="#FFFFCC"];
- O =>> B [label="Sends XmrBidLockSpendTxMessage"],
- C note C2
- [label="The XmrBidLockSpendTxMessage contains the script-coin-lock-tx and the offerer's signature for it.",
- textbgcolor="#FFFFCC"];
- O =>> N [label="Sends script-coin-lock-tx"],
- O abox O [label="Bid Script coin spend tx valid"];
- |||;
- B => B [label="Wait for script-coin-lock-tx to confirm"], O => O [label="Wait for script-coin-lock-tx to confirm"];
- O abox O [label="Bid Script coin locked"];
- B =>> N [label="Sends noscript-coin-lock-tx"];
- |||;
- O => O [label="Wait for noscript-coin-lock-tx to confirm"];
- O abox O [label="Bid Scriptless coin locked"];
- O => B [label="Sends script-coin-lock-tx release message"],
- C note C2
- [label="The XmrBidLockReleaseMessage contains the offerer's OTVES for the script-coin-lock-tx.
- The bidder decodes the offerer's signature from the OTVES.
- When the offerer has the plaintext signature, they can decode the bidder's key for the noscript-lock-tx.",
- textbgcolor="#FFFFCC"];
- O abox O [label="Bid Script coin lock released"];
- B =>> N [label="Sends script-coin-lock-spend-tx"];
- N >> O [label="Detects script-coin-lock-spend-tx"];
- O abox O [label="Bid Script tx redeemed"],
- C note C2
- [label="The offerer extracts the bidder's plaintext signature and derives the bidder's noscript-lock-tx keyhalf.",
- textbgcolor="#FFFFCC"];
- O =>> N [label="Sends noscript-coin-lock-spend-tx"];
- O abox O [label="Bid Scriptless tx redeemed"];
- |||;
- O => O [label="Wait for noscript-coin-lock-spend-tx to confirm"];
- O abox O [label="Bid Completed"];
-}
diff --git a/docker/production/swapclient/Dockerfile b/docker/production/swapclient/Dockerfile
index 51a42ba..110e5a3 100644
--- a/docker/production/swapclient/Dockerfile
+++ b/docker/production/swapclient/Dockerfile
@@ -15,9 +15,10 @@ RUN wget -O protobuf_src.tar.gz https://github.com/protocolbuffers/protobuf/rele
make install && \
ldconfig
-RUN wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/refs/tags/anonswap_v0.1.zip && \
- unzip -d coincurve-anonswap coincurve-anonswap.zip && \
- mv ./coincurve-anonswap/*/{.,}* ./coincurve-anonswap || true && \
+ARG COINCURVE_VERSION=v0.1
+RUN wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/refs/tags/anonswap_$COINCURVE_VERSION.zip && \
+ unzip coincurve-anonswap.zip && \
+ mv ./coincurve-anonswap_$COINCURVE_VERSION ./coincurve-anonswap && \
cd coincurve-anonswap && \
python3 setup.py install --force
diff --git a/tests/basicswap/common_xmr.py b/tests/basicswap/common_xmr.py
index 1d4b5c0..52287b1 100644
--- a/tests/basicswap/common_xmr.py
+++ b/tests/basicswap/common_xmr.py
@@ -116,6 +116,8 @@ class XmrTestBase(unittest.TestCase):
settings['min_delay_event'] = 1
settings['max_delay_event'] = 4
+ settings['min_delay_event_short'] = 1
+ settings['max_delay_event_short'] = 4
settings['min_delay_retry'] = 10
settings['max_delay_retry'] = 20
diff --git a/tests/basicswap/extended/test_network.py b/tests/basicswap/extended/test_network.py
index e5cc14b..25f4ddf 100644
--- a/tests/basicswap/extended/test_network.py
+++ b/tests/basicswap/extended/test_network.py
@@ -115,6 +115,8 @@ def prepare_swapclient_dir(datadir, node_id, network_key, network_pubkey):
'check_xmr_swaps_seconds': 1,
'min_delay_event': 1,
'max_delay_event': 5,
+ 'min_delay_event_short': 1,
+ 'max_delay_event_short': 5,
'min_delay_retry': 2,
'max_delay_retry': 10
}
diff --git a/tests/basicswap/extended/test_xmr_persistent.py b/tests/basicswap/extended/test_xmr_persistent.py
index b40b95d..a22de8c 100644
--- a/tests/basicswap/extended/test_xmr_persistent.py
+++ b/tests/basicswap/extended/test_xmr_persistent.py
@@ -222,6 +222,8 @@ class Test(unittest.TestCase):
settings['min_delay_event'] = 1
settings['max_delay_event'] = 4
+ settings['min_delay_event_short'] = 1
+ settings['max_delay_event_short'] = 4
settings['min_delay_retry'] = 15
settings['max_delay_retry'] = 30
settings['min_sequence_lock_seconds'] = 60
diff --git a/tests/basicswap/test_xmr.py b/tests/basicswap/test_xmr.py
index be073d7..57d2d4d 100644
--- a/tests/basicswap/test_xmr.py
+++ b/tests/basicswap/test_xmr.py
@@ -192,6 +192,8 @@ def prepare_swapclient_dir(datadir, node_id, network_key, network_pubkey, with_l
'check_xmr_swaps_seconds': 1,
'min_delay_event': 1,
'max_delay_event': 5,
+ 'min_delay_event_short': 1,
+ 'max_delay_event_short': 5,
'min_delay_retry': 2,
'max_delay_retry': 10,
'debug_ui': True,