Call setLastHeightChecked() in watchXmrSwap()
Remove old notes and config. New html template for XMR bids. Check the mempool for lock spend txid. Retry sepnding coin B lock tx.
This commit is contained in:
parent
4c200fe8d4
commit
2346858145
@ -6,6 +6,7 @@ lint_task:
|
|||||||
- pip install flake8
|
- pip install flake8
|
||||||
- pip install codespell
|
- pip install codespell
|
||||||
script:
|
script:
|
||||||
|
- flake8 --version
|
||||||
- PYTHONWARNINGS="ignore" flake8 --ignore=E501,F841,W503 --exclude=basicswap/contrib,messages_pb2.py,.eggs
|
- PYTHONWARNINGS="ignore" flake8 --ignore=E501,F841,W503 --exclude=basicswap/contrib,messages_pb2.py,.eggs
|
||||||
- codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=tests/lint/spelling.ignore-words.txt -S .git,.eggs,gitianpubkeys,*.pyc,*basicswap/contrib,*mnemonics.py
|
- codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=tests/lint/spelling.ignore-words.txt -S .git,.eggs,gitianpubkeys,*.pyc,*basicswap/contrib,*mnemonics.py
|
||||||
|
|
||||||
@ -23,7 +24,8 @@ test_task:
|
|||||||
- LITECOIN_BINDIR: ${BIN_DIRS}/litecoin-${LTC_VERSION}/bin
|
- LITECOIN_BINDIR: ${BIN_DIRS}/litecoin-${LTC_VERSION}/bin
|
||||||
- MONERO_BINDIR: ${BIN_DIRS}/monero-${XMR_VERSION}/bin
|
- MONERO_BINDIR: ${BIN_DIRS}/monero-${XMR_VERSION}/bin
|
||||||
setup_script:
|
setup_script:
|
||||||
- sudo apt-get install -y wget python3-pip gnupg unzip protobuf-compiler automake libtool pkg-config
|
- apt-get update
|
||||||
|
- apt-get install -y wget python3-pip gnupg unzip protobuf-compiler automake libtool pkg-config
|
||||||
- if [ ! -d "$BIN_DIRS" ]; then mkdir -p "$BIN_DIRS" ; fi
|
- if [ ! -d "$BIN_DIRS" ]; then mkdir -p "$BIN_DIRS" ; fi
|
||||||
- if [ ! -d "$PARTICL_BINDIR" ]; then cd "$BIN_DIRS" && wget https://github.com/tecnovert/particl-core/releases/download/v${PART_VERSION}/particl-${PART_VERSION}-x86_64-linux-gnu.tar.gz && tar xvf particl-${PART_VERSION}-x86_64-linux-gnu.tar.gz; fi
|
- if [ ! -d "$PARTICL_BINDIR" ]; then cd "$BIN_DIRS" && wget https://github.com/tecnovert/particl-core/releases/download/v${PART_VERSION}/particl-${PART_VERSION}-x86_64-linux-gnu.tar.gz && tar xvf particl-${PART_VERSION}-x86_64-linux-gnu.tar.gz; fi
|
||||||
- if [ ! -d "$BITCOIN_BINDIR" ]; then cd "$BIN_DIRS" && wget https://bitcoincore.org/bin/bitcoin-core-${BTC_VERSION}/bitcoin-${BTC_VERSION}-x86_64-linux-gnu.tar.gz && tar xvf bitcoin-${BTC_VERSION}-x86_64-linux-gnu.tar.gz; fi
|
- if [ ! -d "$BITCOIN_BINDIR" ]; then cd "$BIN_DIRS" && wget https://bitcoincore.org/bin/bitcoin-core-${BTC_VERSION}/bitcoin-${BTC_VERSION}-x86_64-linux-gnu.tar.gz && tar xvf bitcoin-${BTC_VERSION}-x86_64-linux-gnu.tar.gz; fi
|
||||||
|
@ -49,7 +49,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
cache: false
|
cache: false
|
||||||
install:
|
install:
|
||||||
- travis_retry pip install flake8==3.5.0
|
- travis_retry pip install flake8==3.7.0
|
||||||
- travis_retry pip install codespell==1.15.0
|
- travis_retry pip install codespell==1.15.0
|
||||||
before_script:
|
before_script:
|
||||||
script:
|
script:
|
||||||
|
@ -184,6 +184,9 @@ class EventTypes(IntEnum):
|
|||||||
|
|
||||||
class EventLogTypes(IntEnum):
|
class EventLogTypes(IntEnum):
|
||||||
FAILED_TX_B_PUBLISH = auto()
|
FAILED_TX_B_PUBLISH = auto()
|
||||||
|
LOCK_TX_A_PUBLISHED = auto()
|
||||||
|
LOCK_TX_B_PUBLISHED = auto()
|
||||||
|
FAILED_TX_B_SPEND = auto()
|
||||||
|
|
||||||
|
|
||||||
class XmrSplitMsgTypes(IntEnum):
|
class XmrSplitMsgTypes(IntEnum):
|
||||||
@ -278,6 +281,22 @@ def strTxState(state):
|
|||||||
return 'Unknown'
|
return 'Unknown'
|
||||||
|
|
||||||
|
|
||||||
|
def strTxType(tx_type):
|
||||||
|
if tx_type == TxTypes.XMR_SWAP_A_LOCK:
|
||||||
|
return 'Chain A Lock Tx'
|
||||||
|
if tx_type == TxTypes.XMR_SWAP_A_LOCK_SPEND:
|
||||||
|
return 'Chain A Lock Spend Tx'
|
||||||
|
if tx_type == TxTypes.XMR_SWAP_A_LOCK_REFUND:
|
||||||
|
return 'Chain A Lock Refund Tx'
|
||||||
|
if tx_type == TxTypes.XMR_SWAP_A_LOCK_REFUND_SPEND:
|
||||||
|
return 'Chain A Lock Refund Spend Tx'
|
||||||
|
if tx_type == TxTypes.XMR_SWAP_A_LOCK_REFUND_SWIPE:
|
||||||
|
return 'Chain A Lock Refund Swipe Tx'
|
||||||
|
if tx_type == TxTypes.XMR_SWAP_B_LOCK:
|
||||||
|
return 'Chain B Lock Tx'
|
||||||
|
return 'Unknown'
|
||||||
|
|
||||||
|
|
||||||
def getLockName(lock_type):
|
def getLockName(lock_type):
|
||||||
if lock_type == SEQUENCE_LOCK_BLOCKS:
|
if lock_type == SEQUENCE_LOCK_BLOCKS:
|
||||||
return 'Sequence lock, blocks'
|
return 'Sequence lock, blocks'
|
||||||
@ -417,8 +436,8 @@ class BasicSwap(BaseApp):
|
|||||||
self.min_delay_event = self.settings.get('min_delay_event', 10)
|
self.min_delay_event = self.settings.get('min_delay_event', 10)
|
||||||
self.max_delay_event = self.settings.get('max_delay_event', 60)
|
self.max_delay_event = self.settings.get('max_delay_event', 60)
|
||||||
|
|
||||||
self.min_delay_retry = self.settings.get('min_delay_retry', 20)
|
self.min_delay_retry = self.settings.get('min_delay_retry', 60)
|
||||||
self.max_delay_retry = self.settings.get('max_delay_retry', 120)
|
self.max_delay_retry = self.settings.get('max_delay_retry', 5 * 60)
|
||||||
|
|
||||||
self.swaps_in_progress = dict()
|
self.swaps_in_progress = dict()
|
||||||
|
|
||||||
@ -1148,7 +1167,7 @@ class BasicSwap(BaseApp):
|
|||||||
self.mxDB.acquire()
|
self.mxDB.acquire()
|
||||||
try:
|
try:
|
||||||
session = scoped_session(self.session_factory)
|
session = scoped_session(self.session_factory)
|
||||||
record = session.query(PooledAddress).filter(sa.and_(PooledAddress.coin_type == int(coin_type), PooledAddress.bid_id == None)).first() # noqa E712
|
record = session.query(PooledAddress).filter(sa.and_(PooledAddress.coin_type == int(coin_type), PooledAddress.bid_id == None)).first() # noqa: E712,E711
|
||||||
if not record:
|
if not record:
|
||||||
address = self.getReceiveAddressForCoin(coin_type)
|
address = self.getReceiveAddressForCoin(coin_type)
|
||||||
record = PooledAddress(
|
record = PooledAddress(
|
||||||
@ -1526,10 +1545,15 @@ class BasicSwap(BaseApp):
|
|||||||
try:
|
try:
|
||||||
session = scoped_session(self.session_factory)
|
session = scoped_session(self.session_factory)
|
||||||
bid = session.query(Bid).filter_by(bid_id=bid_id).first()
|
bid = session.query(Bid).filter_by(bid_id=bid_id).first()
|
||||||
|
offer = None
|
||||||
if bid:
|
if bid:
|
||||||
bid.initiate_tx = session.query(SwapTx).filter(sa.and_(SwapTx.bid_id == bid_id, SwapTx.tx_type == TxTypes.ITX)).first()
|
offer = session.query(Offer).filter_by(offer_id=bid.offer_id).first()
|
||||||
bid.participate_tx = session.query(SwapTx).filter(sa.and_(SwapTx.bid_id == bid_id, SwapTx.tx_type == TxTypes.PTX)).first()
|
if offer and offer.swap_type == SwapTypes.XMR_SWAP:
|
||||||
return bid, session.query(Offer).filter_by(offer_id=bid.offer_id).first() if bid is not None else None
|
self.loadBidTxns(bid, session)
|
||||||
|
else:
|
||||||
|
bid.initiate_tx = session.query(SwapTx).filter(sa.and_(SwapTx.bid_id == bid_id, SwapTx.tx_type == TxTypes.ITX)).first()
|
||||||
|
bid.participate_tx = session.query(SwapTx).filter(sa.and_(SwapTx.bid_id == bid_id, SwapTx.tx_type == TxTypes.PTX)).first()
|
||||||
|
return bid, offer
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
session.remove()
|
session.remove()
|
||||||
@ -1666,7 +1690,8 @@ class BasicSwap(BaseApp):
|
|||||||
xmr_swap = XmrSwap()
|
xmr_swap = XmrSwap()
|
||||||
xmr_swap.contract_count = self.getNewContractId()
|
xmr_swap.contract_count = self.getNewContractId()
|
||||||
xmr_swap.dest_af = msg_buf.dest_af
|
xmr_swap.dest_af = msg_buf.dest_af
|
||||||
xmr_swap.b_restore_height = self.ci(coin_to).getChainHeight()
|
xmr_swap.start_chain_a_height = ci_from.getChainHeight()
|
||||||
|
xmr_swap.b_restore_height = ci_to.getChainHeight()
|
||||||
kbvf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 1, for_ed25519=True)
|
kbvf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 1, for_ed25519=True)
|
||||||
kbsf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 2, for_ed25519=True)
|
kbsf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 2, for_ed25519=True)
|
||||||
|
|
||||||
@ -2492,33 +2517,33 @@ class BasicSwap(BaseApp):
|
|||||||
bid.setState(BidStates.XMR_SWAP_FAILED_REFUNDED)
|
bid.setState(BidStates.XMR_SWAP_FAILED_REFUNDED)
|
||||||
self.saveBidInSession(bid_id, bid, session, xmr_swap)
|
self.saveBidInSession(bid_id, bid, session, xmr_swap)
|
||||||
session.commit()
|
session.commit()
|
||||||
return rv
|
|
||||||
|
|
||||||
if len(xmr_swap.al_lock_refund_tx_sig) > 0 and len(xmr_swap.af_lock_refund_tx_sig) > 0:
|
|
||||||
try:
|
|
||||||
txid = ci_from.publishTx(xmr_swap.a_lock_refund_tx)
|
|
||||||
|
|
||||||
self.log.info('Submitted coin a lock refund tx for bid {}'.format(bid_id.hex()))
|
|
||||||
bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND] = SwapTx(
|
|
||||||
bid_id=bid_id,
|
|
||||||
tx_type=TxTypes.XMR_SWAP_A_LOCK_REFUND,
|
|
||||||
txid=bytes.fromhex(txid),
|
|
||||||
)
|
|
||||||
self.saveBidInSession(bid_id, bid, session, xmr_swap)
|
|
||||||
session.commit()
|
|
||||||
return rv
|
return rv
|
||||||
except Exception as ex:
|
else: # not XMR_SWAP_A_LOCK_REFUND in bid.txns
|
||||||
if 'Transaction already in block chain' in str(ex):
|
if len(xmr_swap.al_lock_refund_tx_sig) > 0 and len(xmr_swap.af_lock_refund_tx_sig) > 0:
|
||||||
self.log.info('Found coin a lock refund tx for bid {}'.format(bid_id.hex()))
|
try:
|
||||||
txid = ci_from.getTxHash(xmr_swap.a_lock_refund_tx)
|
txid = ci_from.publishTx(xmr_swap.a_lock_refund_tx)
|
||||||
|
|
||||||
|
self.log.info('Submitted coin a lock refund tx for bid {}'.format(bid_id.hex()))
|
||||||
bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND] = SwapTx(
|
bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND] = SwapTx(
|
||||||
bid_id=bid_id,
|
bid_id=bid_id,
|
||||||
tx_type=TxTypes.XMR_SWAP_A_LOCK_REFUND,
|
tx_type=TxTypes.XMR_SWAP_A_LOCK_REFUND,
|
||||||
txid=txid,
|
txid=bytes.fromhex(txid),
|
||||||
)
|
)
|
||||||
self.saveBidInSession(bid_id, bid, session, xmr_swap)
|
self.saveBidInSession(bid_id, bid, session, xmr_swap)
|
||||||
session.commit()
|
session.commit()
|
||||||
return rv
|
return rv
|
||||||
|
except Exception as ex:
|
||||||
|
if 'Transaction already in block chain' in str(ex):
|
||||||
|
self.log.info('Found coin a lock refund tx for bid {}'.format(bid_id.hex()))
|
||||||
|
txid = ci_from.getTxHash(xmr_swap.a_lock_refund_tx)
|
||||||
|
bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND] = SwapTx(
|
||||||
|
bid_id=bid_id,
|
||||||
|
tx_type=TxTypes.XMR_SWAP_A_LOCK_REFUND,
|
||||||
|
txid=txid,
|
||||||
|
)
|
||||||
|
self.saveBidInSession(bid_id, bid, session, xmr_swap)
|
||||||
|
session.commit()
|
||||||
|
return rv
|
||||||
|
|
||||||
state = BidStates(bid.state)
|
state = BidStates(bid.state)
|
||||||
if state == BidStates.SWAP_COMPLETED:
|
if state == BidStates.SWAP_COMPLETED:
|
||||||
@ -2585,7 +2610,14 @@ class BasicSwap(BaseApp):
|
|||||||
elif state == BidStates.XMR_SWAP_SECRET_SHARED:
|
elif state == BidStates.XMR_SWAP_SECRET_SHARED:
|
||||||
# Wait for script spend tx to confirm
|
# Wait for script spend tx to confirm
|
||||||
# TODO: Use explorer to get tx / block hash for getrawtransaction
|
# TODO: Use explorer to get tx / block hash for getrawtransaction
|
||||||
pass
|
|
||||||
|
if bid.was_received:
|
||||||
|
try:
|
||||||
|
txn_hex = ci_from.getMempoolTx(xmr_swap.a_lock_spend_tx_id)
|
||||||
|
self.log.info('Found lock spend txn in %s mempool, %s', ci_from.coin_name(), xmr_swap.a_lock_spend_tx_id.hex())
|
||||||
|
self.process_XMR_SWAP_A_LOCK_tx_spend(bid_id, xmr_swap.a_lock_spend_tx_id.hex(), txn_hex)
|
||||||
|
except Exception as e:
|
||||||
|
self.log.debug('getrawtransaction lock spend tx failed: %s', str(e))
|
||||||
elif state == BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED:
|
elif state == BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED:
|
||||||
txid_hex = bid.xmr_b_lock_tx.spend_txid.hex()
|
txid_hex = bid.xmr_b_lock_tx.spend_txid.hex()
|
||||||
|
|
||||||
@ -2604,6 +2636,7 @@ class BasicSwap(BaseApp):
|
|||||||
session.close()
|
session.close()
|
||||||
session.remove()
|
session.remove()
|
||||||
self.mxDB.release()
|
self.mxDB.release()
|
||||||
|
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
def checkBidState(self, bid_id, bid, offer):
|
def checkBidState(self, bid_id, bid, offer):
|
||||||
@ -2858,7 +2891,7 @@ class BasicSwap(BaseApp):
|
|||||||
self.removeWatchedOutput(coin_to, bid_id, bid.participate_tx.txid.hex())
|
self.removeWatchedOutput(coin_to, bid_id, bid.participate_tx.txid.hex())
|
||||||
self.saveBid(bid_id, bid)
|
self.saveBid(bid_id, bid)
|
||||||
|
|
||||||
def process_XMR_SWAP_A_LOCK_tx_spend(self, bid_id, spend_txid_hex, spend_txn):
|
def process_XMR_SWAP_A_LOCK_tx_spend(self, bid_id, spend_txid_hex, spend_txn_hex):
|
||||||
self.log.debug('Detected spend of XMR swap coin a lock tx for bid %s', bid_id.hex())
|
self.log.debug('Detected spend of XMR swap coin a lock tx for bid %s', bid_id.hex())
|
||||||
self.mxDB.acquire()
|
self.mxDB.acquire()
|
||||||
try:
|
try:
|
||||||
@ -2877,15 +2910,14 @@ class BasicSwap(BaseApp):
|
|||||||
spending_txid = bytes.fromhex(spend_txid_hex)
|
spending_txid = bytes.fromhex(spend_txid_hex)
|
||||||
|
|
||||||
if spending_txid == xmr_swap.a_lock_spend_tx_id:
|
if spending_txid == xmr_swap.a_lock_spend_tx_id:
|
||||||
self.log.debug('[rm] state %d', state)
|
|
||||||
self.log.debug('[rm] BidStates.XMR_SWAP_SECRET_SHARED %d', BidStates.XMR_SWAP_SECRET_SHARED)
|
|
||||||
if state == BidStates.XMR_SWAP_SECRET_SHARED:
|
if state == BidStates.XMR_SWAP_SECRET_SHARED:
|
||||||
xmr_swap.a_lock_spend_tx = bytes.fromhex(spend_txn['hex'])
|
xmr_swap.a_lock_spend_tx = bytes.fromhex(spend_txn_hex)
|
||||||
bid.setState(BidStates.XMR_SWAP_SCRIPT_TX_REDEEMED) # TODO: Wait for confirmation?
|
bid.setState(BidStates.XMR_SWAP_SCRIPT_TX_REDEEMED) # TODO: Wait for confirmation?
|
||||||
|
|
||||||
if not bid.was_received:
|
if not bid.was_received:
|
||||||
bid.setState(BidStates.SWAP_COMPLETED)
|
bid.setState(BidStates.SWAP_COMPLETED)
|
||||||
if bid.was_received:
|
if bid.was_received:
|
||||||
|
bid.setState(BidStates.SWAP_DELAYING)
|
||||||
delay = random.randrange(self.min_delay_event, self.max_delay_event)
|
delay = random.randrange(self.min_delay_event, self.max_delay_event)
|
||||||
self.log.info('Redeeming coin b lock tx for bid %s in %d seconds', bid_id.hex(), delay)
|
self.log.info('Redeeming coin b lock tx for bid %s in %d seconds', bid_id.hex(), delay)
|
||||||
self.createEventInSession(delay, EventTypes.REDEEM_XMR_SWAP_LOCK_TX_B, bid_id, session)
|
self.createEventInSession(delay, EventTypes.REDEEM_XMR_SWAP_LOCK_TX_B, bid_id, session)
|
||||||
@ -2968,7 +3000,7 @@ class BasicSwap(BaseApp):
|
|||||||
def processSpentOutput(self, coin_type, watched_output, spend_txid_hex, spend_n, spend_txn):
|
def processSpentOutput(self, coin_type, watched_output, spend_txid_hex, spend_n, spend_txn):
|
||||||
if watched_output.swap_type == SwapTypes.XMR_SWAP:
|
if watched_output.swap_type == SwapTypes.XMR_SWAP:
|
||||||
if watched_output.tx_type == TxTypes.XMR_SWAP_A_LOCK:
|
if watched_output.tx_type == TxTypes.XMR_SWAP_A_LOCK:
|
||||||
self.process_XMR_SWAP_A_LOCK_tx_spend(watched_output.bid_id, spend_txid_hex, spend_txn)
|
self.process_XMR_SWAP_A_LOCK_tx_spend(watched_output.bid_id, spend_txid_hex, spend_txn['hex'])
|
||||||
elif watched_output.tx_type == TxTypes.XMR_SWAP_A_LOCK_REFUND:
|
elif watched_output.tx_type == TxTypes.XMR_SWAP_A_LOCK_REFUND:
|
||||||
self.process_XMR_SWAP_A_LOCK_REFUND_tx_spend(watched_output.bid_id, spend_txid_hex, spend_txn)
|
self.process_XMR_SWAP_A_LOCK_REFUND_tx_spend(watched_output.bid_id, spend_txid_hex, spend_txn)
|
||||||
|
|
||||||
@ -3005,7 +3037,15 @@ class BasicSwap(BaseApp):
|
|||||||
self.log.debug('chain_blocks, last_height_checked %s %s', chain_blocks, last_height_checked)
|
self.log.debug('chain_blocks, last_height_checked %s %s', chain_blocks, last_height_checked)
|
||||||
while last_height_checked < chain_blocks:
|
while last_height_checked < chain_blocks:
|
||||||
block_hash = self.callcoinrpc(coin_type, 'getblockhash', [last_height_checked + 1])
|
block_hash = self.callcoinrpc(coin_type, 'getblockhash', [last_height_checked + 1])
|
||||||
block = self.callcoinrpc(coin_type, 'getblock', [block_hash, 2])
|
try:
|
||||||
|
block = self.callcoinrpc(coin_type, 'getblock', [block_hash, 2])
|
||||||
|
except Exception as e:
|
||||||
|
if 'Block not available (pruned data)' in str(e):
|
||||||
|
# TODO: Better solution?
|
||||||
|
bci = self.callcoinrpc(coin_type, 'getblockchaininfo')
|
||||||
|
self.log.error('Coin %s last_height_checked %d set to pruneheight %d', self.ci(coin_type).coin_name(), last_height_checked, bci['pruneheight'])
|
||||||
|
last_height_checked = bci['pruneheight']
|
||||||
|
continue
|
||||||
|
|
||||||
for tx in block['tx']:
|
for tx in block['tx']:
|
||||||
for i, inp in enumerate(tx['vin']):
|
for i, inp in enumerate(tx['vin']):
|
||||||
@ -3232,6 +3272,11 @@ class BasicSwap(BaseApp):
|
|||||||
try:
|
try:
|
||||||
session = scoped_session(self.session_factory)
|
session = scoped_session(self.session_factory)
|
||||||
|
|
||||||
|
if len(msg_data.offer_msg_id) != 28:
|
||||||
|
raise ValueError('Invalid msg_id length')
|
||||||
|
|
||||||
|
# TODO: Add to db in case received out of order, or add extra startup logic
|
||||||
|
|
||||||
offer = session.query(Offer).filter_by(offer_id=msg_data.offer_msg_id).first()
|
offer = session.query(Offer).filter_by(offer_id=msg_data.offer_msg_id).first()
|
||||||
if offer is None:
|
if offer is None:
|
||||||
raise ValueError('Offer not found: {}'.format(msg_data.offer_msg_id.hex()))
|
raise ValueError('Offer not found: {}'.format(msg_data.offer_msg_id.hex()))
|
||||||
@ -3245,7 +3290,7 @@ class BasicSwap(BaseApp):
|
|||||||
signature_enc = base64.b64encode(msg_data.signature).decode('utf-8')
|
signature_enc = base64.b64encode(msg_data.signature).decode('utf-8')
|
||||||
|
|
||||||
passed = self.callcoinrpc(Coins.PART, 'verifymessage', [offer.addr_from, signature_enc, msg_data.offer_msg_id.hex() + '_revoke'])
|
passed = self.callcoinrpc(Coins.PART, 'verifymessage', [offer.addr_from, signature_enc, msg_data.offer_msg_id.hex() + '_revoke'])
|
||||||
assert(passed is True), 'Proof of funds signature invalid'
|
assert(passed is True), 'Signature invalid'
|
||||||
|
|
||||||
offer.active_ind = 2
|
offer.active_ind = 2
|
||||||
# TODO: Remove message, or wait for expire
|
# TODO: Remove message, or wait for expire
|
||||||
@ -3558,6 +3603,7 @@ class BasicSwap(BaseApp):
|
|||||||
pkbvf=ci_to.getPubkey(bid_data.kbvf),
|
pkbvf=ci_to.getPubkey(bid_data.kbvf),
|
||||||
kbsf_dleag=bid_data.kbsf_dleag,
|
kbsf_dleag=bid_data.kbsf_dleag,
|
||||||
b_restore_height=ci_to.getChainHeight(),
|
b_restore_height=ci_to.getChainHeight(),
|
||||||
|
start_chain_a_height=ci_from.getChainHeight(),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
bid.created_at = msg['sent']
|
bid.created_at = msg['sent']
|
||||||
@ -3650,8 +3696,11 @@ class BasicSwap(BaseApp):
|
|||||||
def watchXmrSwap(self, bid, offer, xmr_swap):
|
def watchXmrSwap(self, bid, offer, xmr_swap):
|
||||||
self.log.debug('XMR swap in progress, bid %s', bid.bid_id.hex())
|
self.log.debug('XMR swap in progress, bid %s', bid.bid_id.hex())
|
||||||
self.swaps_in_progress[bid.bid_id] = (bid, offer)
|
self.swaps_in_progress[bid.bid_id] = (bid, offer)
|
||||||
self.addWatchedOutput(Coins(offer.coin_from), bid.bid_id, bid.xmr_a_lock_tx.txid.hex(), bid.xmr_a_lock_tx.vout, TxTypes.XMR_SWAP_A_LOCK, SwapTypes.XMR_SWAP)
|
|
||||||
self.addWatchedOutput(Coins(offer.coin_from), bid.bid_id, xmr_swap.a_lock_refund_tx_id.hex(), 0, TxTypes.XMR_SWAP_A_LOCK_REFUND, SwapTypes.XMR_SWAP)
|
coin_from = Coins(offer.coin_from)
|
||||||
|
self.setLastHeightChecked(coin_from, xmr_swap.start_chain_a_height)
|
||||||
|
self.addWatchedOutput(coin_from, bid.bid_id, bid.xmr_a_lock_tx.txid.hex(), bid.xmr_a_lock_tx.vout, TxTypes.XMR_SWAP_A_LOCK, SwapTypes.XMR_SWAP)
|
||||||
|
self.addWatchedOutput(coin_from, bid.bid_id, xmr_swap.a_lock_refund_tx_id.hex(), 0, TxTypes.XMR_SWAP_A_LOCK_REFUND, SwapTypes.XMR_SWAP)
|
||||||
bid.in_progress = 1
|
bid.in_progress = 1
|
||||||
|
|
||||||
def sendXmrBidTxnSigsFtoL(self, bid_id, session):
|
def sendXmrBidTxnSigsFtoL(self, bid_id, session):
|
||||||
@ -3938,7 +3987,28 @@ class BasicSwap(BaseApp):
|
|||||||
|
|
||||||
address_to = ci_to.getMainWalletAddress()
|
address_to = ci_to.getMainWalletAddress()
|
||||||
|
|
||||||
txid = ci_to.spendBLockTx(address_to, xmr_swap.vkbv, vkbs, bid.amount_to, xmr_offer.b_fee_rate, xmr_swap.b_restore_height)
|
try:
|
||||||
|
txid = ci_to.spendBLockTx(address_to, xmr_swap.vkbv, vkbs, bid.amount_to, xmr_offer.b_fee_rate, xmr_swap.b_restore_height)
|
||||||
|
self.log.debug('Submitted lock B spend txn %s to %s chain for bid %s', txid.hex(), ci_to.coin_name(), bid_id.hex())
|
||||||
|
except Exception as ex:
|
||||||
|
# TODO: Make min-conf 10?
|
||||||
|
error_msg = 'spendBLockTx failed for bid {} with error {}'.format(bid_id.hex(), str(ex))
|
||||||
|
num_retries = self.countBidEvents(bid, EventLogTypes.FAILED_TX_B_SPEND, session)
|
||||||
|
if num_retries > 0:
|
||||||
|
error_msg += ', retry no. {}'.format(num_retries)
|
||||||
|
self.log.error(error_msg)
|
||||||
|
|
||||||
|
str_error = str(ex)
|
||||||
|
if num_retries < 100 and 'Invalid unlocked_balance' in str_error:
|
||||||
|
delay = random.randrange(self.min_delay_retry, self.max_delay_retry)
|
||||||
|
self.log.info('Retrying sending xmr swap chain B spend tx for bid %s in %d seconds', bid_id.hex(), delay)
|
||||||
|
self.createEventInSession(delay, EventTypes.REDEEM_XMR_SWAP_LOCK_TX_B, bid_id, session)
|
||||||
|
else:
|
||||||
|
self.setBidError(bid_id, bid, 'spendBLockTx failed: ' + str(ex), save_bid=False)
|
||||||
|
self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer)
|
||||||
|
|
||||||
|
self.logBidEvent(bid, EventLogTypes.FAILED_TX_B_SPEND, str_error, session)
|
||||||
|
return
|
||||||
|
|
||||||
bid.xmr_b_lock_tx.spend_txid = txid
|
bid.xmr_b_lock_tx.spend_txid = txid
|
||||||
bid.setState(BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED)
|
bid.setState(BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED)
|
||||||
@ -4176,7 +4246,17 @@ class BasicSwap(BaseApp):
|
|||||||
|
|
||||||
msg_id = message[2:]
|
msg_id = message[2:]
|
||||||
options = {'encoding': 'hex', 'setread': True}
|
options = {'encoding': 'hex', 'setread': True}
|
||||||
msg = self.callrpc('smsg', [msg_id.hex(), options])
|
num_tries = 5
|
||||||
|
for i in range(num_tries + 1):
|
||||||
|
try:
|
||||||
|
msg = self.callrpc('smsg', [msg_id.hex(), options])
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
if 'Unknown message id' in str(e) and i < num_tries:
|
||||||
|
time.sleep(1)
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
self.processMsg(msg)
|
self.processMsg(msg)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
@ -4249,7 +4329,6 @@ class BasicSwap(BaseApp):
|
|||||||
if bid.state != data['bid_state']:
|
if bid.state != data['bid_state']:
|
||||||
bid.setState(data['bid_state'])
|
bid.setState(data['bid_state'])
|
||||||
self.log.debug('Set state to %s', strBidState(bid.state))
|
self.log.debug('Set state to %s', strBidState(bid.state))
|
||||||
self.log.debug('[rm] Set bid.state %d', bid.state)
|
|
||||||
has_changed = True
|
has_changed = True
|
||||||
|
|
||||||
if has_changed:
|
if has_changed:
|
||||||
@ -4262,7 +4341,7 @@ class BasicSwap(BaseApp):
|
|||||||
else:
|
else:
|
||||||
self.log.debug('TODO - determine in-progress for manualBidUpdate')
|
self.log.debug('TODO - determine in-progress for manualBidUpdate')
|
||||||
if offer.swap_type == SwapTypes.XMR_SWAP:
|
if offer.swap_type == SwapTypes.XMR_SWAP:
|
||||||
if bid.state and bid.state == BidStates.XMR_SWAP_SECRET_SHARED:
|
if bid.state and bid.state in (BidStates.XMR_SWAP_SECRET_SHARED, BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED):
|
||||||
activate_bid = True
|
activate_bid = True
|
||||||
|
|
||||||
if activate_bid:
|
if activate_bid:
|
||||||
@ -4270,7 +4349,6 @@ class BasicSwap(BaseApp):
|
|||||||
else:
|
else:
|
||||||
self.deactivateBid(session, offer, bid)
|
self.deactivateBid(session, offer, bid)
|
||||||
|
|
||||||
self.log.debug('[rm] bid.state %d', bid.state)
|
|
||||||
self.saveBidInSession(bid_id, bid, session)
|
self.saveBidInSession(bid_id, bid, session)
|
||||||
session.commit()
|
session.commit()
|
||||||
finally:
|
finally:
|
||||||
@ -4381,7 +4459,7 @@ class BasicSwap(BaseApp):
|
|||||||
session = scoped_session(self.session_factory)
|
session = scoped_session(self.session_factory)
|
||||||
|
|
||||||
if sent:
|
if sent:
|
||||||
q = session.query(Offer).filter(Offer.was_sent == True) # noqa E712
|
q = session.query(Offer).filter(Offer.was_sent == True) # noqa: E712
|
||||||
else:
|
else:
|
||||||
q = session.query(Offer).filter(sa.and_(Offer.expire_at > now, Offer.active_ind == 1))
|
q = session.query(Offer).filter(sa.and_(Offer.expire_at > now, Offer.active_ind == 1))
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ from sqlalchemy.ext.declarative import declarative_base
|
|||||||
from enum import IntEnum, auto
|
from enum import IntEnum, auto
|
||||||
|
|
||||||
|
|
||||||
CURRENT_DB_VERSION = 3
|
CURRENT_DB_VERSION = 4
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
@ -317,7 +317,8 @@ class XmrSwap(Base):
|
|||||||
|
|
||||||
b_lock_tx_id = sa.Column(sa.LargeBinary)
|
b_lock_tx_id = sa.Column(sa.LargeBinary)
|
||||||
|
|
||||||
b_restore_height = sa.Column(sa.Integer) # Height of xmr chain before the swap
|
start_chain_a_height = sa.Column(sa.Integer) # Height of script chain before the swap
|
||||||
|
b_restore_height = sa.Column(sa.Integer) # Height of scriptless chain before the swap
|
||||||
|
|
||||||
|
|
||||||
class XmrSplitData(Base):
|
class XmrSplitData(Base):
|
||||||
@ -331,6 +332,16 @@ class XmrSplitData(Base):
|
|||||||
created_at = sa.Column(sa.BigInteger)
|
created_at = sa.Column(sa.BigInteger)
|
||||||
|
|
||||||
|
|
||||||
|
class RevokedMessage(Base):
|
||||||
|
__tablename__ = 'revoked_messages'
|
||||||
|
|
||||||
|
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||||
|
active_ind = sa.Column(sa.Integer)
|
||||||
|
msg_id = sa.Column(sa.LargeBinary)
|
||||||
|
created_at = sa.Column(sa.BigInteger)
|
||||||
|
expires_at = sa.Column(sa.BigInteger)
|
||||||
|
|
||||||
|
|
||||||
class TableTypes(IntEnum):
|
class TableTypes(IntEnum):
|
||||||
OFFER = auto()
|
OFFER = auto()
|
||||||
BID = auto()
|
BID = auto()
|
||||||
|
@ -558,7 +558,7 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||||||
if len(old_states) > 0:
|
if len(old_states) > 0:
|
||||||
old_states.sort(key=lambda x: x[0])
|
old_states.sort(key=lambda x: x[0])
|
||||||
|
|
||||||
template = env.get_template('bid.html')
|
template = env.get_template('bid_xmr.html') if offer.swap_type == SwapTypes.XMR_SWAP else env.get_template('bid.html')
|
||||||
return bytes(template.render(
|
return bytes(template.render(
|
||||||
title=self.server.title,
|
title=self.server.title,
|
||||||
h2=self.server.title,
|
h2=self.server.title,
|
||||||
|
@ -134,6 +134,12 @@ class BTCInterface(CoinInterface):
|
|||||||
def getBlockchainInfo(self):
|
def getBlockchainInfo(self):
|
||||||
return self.rpc_callback('getblockchaininfo')
|
return self.rpc_callback('getblockchaininfo')
|
||||||
|
|
||||||
|
def getChainHeight(self):
|
||||||
|
return self.rpc_callback('getblockchaininfo')['blocks']
|
||||||
|
|
||||||
|
def getMempoolTx(self, txid):
|
||||||
|
return self.rpc_callback('getrawtransaction', [txid.hex()])
|
||||||
|
|
||||||
def initialiseWallet(self, key_bytes):
|
def initialiseWallet(self, key_bytes):
|
||||||
wif_prefix = chainparams[self.coin_type()][self._network]['key_prefix']
|
wif_prefix = chainparams[self.coin_type()][self._network]['key_prefix']
|
||||||
key_wif = toWIF(wif_prefix, key_bytes)
|
key_wif = toWIF(wif_prefix, key_bytes)
|
||||||
|
@ -372,6 +372,9 @@ class XMRInterface(CoinInterface):
|
|||||||
if rv['balance'] < cb_swap_value:
|
if rv['balance'] < cb_swap_value:
|
||||||
logging.error('wallet {} balance {}, expected {}'.format(wallet_filename, rv['balance'], cb_swap_value))
|
logging.error('wallet {} balance {}, expected {}'.format(wallet_filename, rv['balance'], cb_swap_value))
|
||||||
raise ValueError('Invalid balance')
|
raise ValueError('Invalid balance')
|
||||||
|
if rv['unlocked_balance'] < cb_swap_value:
|
||||||
|
logging.error('wallet {} balance {}, expected {}, blocks_to_unlock {}'.format(wallet_filename, rv['unlocked_balance'], cb_swap_value, rv['blocks_to_unlock']))
|
||||||
|
raise ValueError('Invalid unlocked_balance')
|
||||||
|
|
||||||
params = {'address': address_to}
|
params = {'address': address_to}
|
||||||
rv = self.rpc_wallet_cb('sweep_all', params)
|
rv = self.rpc_wallet_cb('sweep_all', params)
|
||||||
|
72
basicswap/templates/bid_xmr.html
Normal file
72
basicswap/templates/bid_xmr.html
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
{% include 'header.html' %}
|
||||||
|
|
||||||
|
<h3>Bid {{ bid_id }}</h3>
|
||||||
|
{% if refresh %}
|
||||||
|
<p>Page Refresh: {{ refresh }} seconds</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% for m in messages %}
|
||||||
|
<p>{{ m }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><td>Swap</td><td>{{ data.amt_from }} {{ data.ticker_from }} for {{ data.amt_to }} {{ data.ticker_to }}</td></tr>
|
||||||
|
<tr><td>Bid State</td><td>{{ data.bid_state }}</td></tr>
|
||||||
|
<tr><td>StateDescription </td><td>{{ data.state_description }}</td></tr>
|
||||||
|
<tr><td>Offer</td><td><a href="/offer/{{ data.offer_id }}">{{ data.offer_id }}</a></td></tr>
|
||||||
|
<tr><td>Address From</td><td>{{ data.addr_from }}</td></tr>
|
||||||
|
<tr><td>Created At</td><td>{{ data.created_at }}</td></tr>
|
||||||
|
<tr><td>Expired At</td><td>{{ data.expired_at }}</td></tr>
|
||||||
|
<tr><td>Sent</td><td>{{ data.was_sent }}</td></tr>
|
||||||
|
<tr><td>Received</td><td>{{ data.was_received }}</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% if data.show_txns %}
|
||||||
|
<h4>Transactions</h4>
|
||||||
|
<table>
|
||||||
|
<tr><th>Tx Type</th><th>Tx ID</th></tr>
|
||||||
|
{% for tx in data.txns %}
|
||||||
|
<tr><td>{{ tx.type }}</td><td>{{ tx.txid }}</td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
{% if edit_bid %}
|
||||||
|
<h4>Edit Bid</h4>
|
||||||
|
<table>
|
||||||
|
<tr><td>Change Bid State</td><td>
|
||||||
|
<select name="new_state">
|
||||||
|
{% for s in data.bid_states %}
|
||||||
|
<option value="{{ s[0] }}"{% if data.bid_state_ind==s[0] %} selected{% endif %}>{{ s[1] }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select></td></tr>
|
||||||
|
</table>
|
||||||
|
<input name="edit_bid_cancel" type="submit" value="Cancel">
|
||||||
|
<input name="edit_bid_submit" type="submit" value="Submit">
|
||||||
|
{% else %}
|
||||||
|
{% if data.was_received == 'True' %}
|
||||||
|
<input name="accept_bid" type="submit" value="Accept Bid"><br/>
|
||||||
|
{% endif %}
|
||||||
|
<input name="abandon_bid" type="submit" value="Abandon Bid">
|
||||||
|
{% if data.show_txns %}
|
||||||
|
<input name="hide_txns" type="submit" value="Hide Info">
|
||||||
|
{% else %}
|
||||||
|
<input name="show_txns" type="submit" value="Show More Info">
|
||||||
|
{% endif %}
|
||||||
|
<input name="edit_bid" type="submit" value="Edit Bid">
|
||||||
|
{% endif %}
|
||||||
|
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<h4>Old States</h4>
|
||||||
|
<table>
|
||||||
|
<tr><th>State</th><th>Set At</th></tr>
|
||||||
|
{% for s in old_states %}
|
||||||
|
<tr><td>{{ s[1] }}</td><td>{{ s[0] | formatts }}</td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p><a href="/">home</a></p>
|
||||||
|
</body></html>
|
@ -13,9 +13,11 @@ from .chainparams import (
|
|||||||
Coins,
|
Coins,
|
||||||
)
|
)
|
||||||
from .basicswap import (
|
from .basicswap import (
|
||||||
|
SwapTypes,
|
||||||
BidStates,
|
BidStates,
|
||||||
TxStates,
|
TxStates,
|
||||||
TxTypes,
|
TxTypes,
|
||||||
|
strTxType,
|
||||||
strBidState,
|
strBidState,
|
||||||
strTxState,
|
strTxState,
|
||||||
)
|
)
|
||||||
@ -147,9 +149,24 @@ def describeBid(swap_client, bid, offer, edit_bid, show_txns):
|
|||||||
data['bid_states'] = listBidStates()
|
data['bid_states'] = listBidStates()
|
||||||
|
|
||||||
if show_txns:
|
if show_txns:
|
||||||
data['initiate_tx_refund'] = 'None' if not bid.initiate_txn_refund else bid.initiate_txn_refund.hex()
|
if offer.swap_type == SwapTypes.XMR_SWAP:
|
||||||
data['participate_tx_refund'] = 'None' if not bid.participate_txn_refund else bid.participate_txn_refund.hex()
|
txns = []
|
||||||
data['initiate_tx_spend'] = getTxSpendHex(bid, TxTypes.ITX)
|
if bid.xmr_a_lock_tx:
|
||||||
data['participate_tx_spend'] = getTxSpendHex(bid, TxTypes.PTX)
|
txns.append({'type': 'Chain A Lock', 'txid': bid.xmr_a_lock_tx.txid.hex()})
|
||||||
|
if bid.xmr_a_lock_spend_tx:
|
||||||
|
txns.append({'type': 'Chain A Lock Spend', 'txid': bid.xmr_a_lock_spend_tx.txid.hex()})
|
||||||
|
if bid.xmr_b_lock_tx:
|
||||||
|
txns.append({'type': 'Chain B Lock', 'txid': bid.xmr_b_lock_tx.txid.hex()})
|
||||||
|
if bid.xmr_b_lock_tx and bid.xmr_b_lock_tx.spend_txid:
|
||||||
|
txns.append({'type': 'Chain B Lock Spend', 'txid': bid.xmr_b_lock_tx.spend_txid.hex()})
|
||||||
|
|
||||||
|
for tx_type, tx in bid.txns.items():
|
||||||
|
txns.append({'type': strTxType(tx_type), 'txid': tx.txid.hex()})
|
||||||
|
data['txns'] = txns
|
||||||
|
else:
|
||||||
|
data['initiate_tx_refund'] = 'None' if not bid.initiate_txn_refund else bid.initiate_txn_refund.hex()
|
||||||
|
data['participate_tx_refund'] = 'None' if not bid.participate_txn_refund else bid.participate_txn_refund.hex()
|
||||||
|
data['initiate_tx_spend'] = getTxSpendHex(bid, TxTypes.ITX)
|
||||||
|
data['participate_tx_spend'] = getTxSpendHex(bid, TxTypes.PTX)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
@ -5,11 +5,6 @@
|
|||||||
# 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.
|
||||||
|
|
||||||
"""
|
|
||||||
Atomic Swap Client - Proof of Concept
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
@ -548,6 +543,7 @@ def main():
|
|||||||
'datadir': os.path.join(data_dir, 'monero'),
|
'datadir': os.path.join(data_dir, 'monero'),
|
||||||
'bindir': os.path.join(bin_dir, 'monero'),
|
'bindir': os.path.join(bin_dir, 'monero'),
|
||||||
'restore_height': xmr_restore_height,
|
'restore_height': xmr_restore_height,
|
||||||
|
'blocks_confirmed': 7, # TODO: 10?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,11 +5,6 @@
|
|||||||
# 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.
|
||||||
|
|
||||||
"""
|
|
||||||
Atomic Swap Client - Proof of Concept
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
|
|
||||||
# Docker Setup
|
|
||||||
|
|
||||||
## Build the images
|
|
||||||
```
|
|
||||||
$ docker-compose build
|
|
||||||
```
|
|
||||||
|
|
||||||
## Prepare the binaries, coin dirs and settings
|
|
||||||
```
|
|
||||||
$ docker run -t --name swap_prepare -v /tmp/coindata:/coindata i_swapclient basicswap-prepare --datadir=/coindata --withcoins=monero --withoutcoins=litecoin
|
|
||||||
```
|
|
||||||
|
|
||||||
Record the mnemonic from the output of the above command.
|
|
||||||
|
|
||||||
Remove swap_prepare container (and logs):
|
|
||||||
```
|
|
||||||
$ docker rm swap_prepare
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
# Running on windows 10
|
|
||||||
|
|
||||||
|
|
||||||
Work in progress - doesn't work reliably.
|
|
||||||
|
|
||||||
|
|
||||||
Install the latest docker toolbox from:
|
|
||||||
https://github.com/docker/toolbox/releases
|
|
||||||
|
|
||||||
Start docker through the desktop icon, it should open a terminal
|
|
||||||
|
|
||||||
Download basicswap
|
|
||||||
https://github.com/tecnovert/basicswap/archive/master.zip
|
|
||||||
|
|
||||||
Extract it.
|
|
||||||
|
|
||||||
Navigate to the docker folder.
|
|
||||||
|
|
||||||
|
|
||||||
If you have an existing litecoin chain, copy the contents of your datadir excluding litecoin.conf and any wallets to coindata/litecoin
|
|
||||||
If your litecoin chain is pruned create a new wallet in the existing datadir to avoid having to resync the chain.
|
|
||||||
|
|
||||||
|
|
||||||
Right click -> properties on the coindata folder, in the security tab make sure all users have 'Modify' rights.
|
|
||||||
|
|
||||||
|
|
||||||
Run the script: dockerbuild.bat
|
|
||||||
|
|
||||||
It should open a new terminal window and start building the container.
|
|
||||||
|
|
||||||
Once that completes run: dockerup.bat
|
|
||||||
|
|
||||||
In the terminal that opened for docker toolbox, find the line:
|
|
||||||
docker is configured to use the default machine with IP 192.168.99.100
|
|
||||||
|
|
||||||
And open the ip address it displays at port 12700 in a browser:
|
|
||||||
192.168.99.100:12700
|
|
||||||
|
|
||||||
Should show some html.
|
|
||||||
|
|
||||||
Now go to the view wallets page, and wait for all chains to completely sync.
|
|
@ -7,6 +7,7 @@
|
|||||||
## Run Using Docker
|
## Run Using Docker
|
||||||
|
|
||||||
Docker must be installed and started:
|
Docker must be installed and started:
|
||||||
|
|
||||||
$ sudo systemctl status docker | grep Active
|
$ sudo systemctl status docker | grep Active
|
||||||
|
|
||||||
Should return a line containing `active (running)`
|
Should return a line containing `active (running)`
|
||||||
@ -20,8 +21,8 @@ Create the images:
|
|||||||
Prepare the datadir:
|
Prepare the datadir:
|
||||||
Set XMR_RPC_HOST and BASE_XMR_RPC_PORT to a public XMR node or exclude to run a local node.
|
Set XMR_RPC_HOST and BASE_XMR_RPC_PORT to a public XMR node or exclude to run a local node.
|
||||||
|
|
||||||
$ export SWAP_DATADIR=/var/data/coinswaps
|
$ export COINDATA_PATH=/var/data/coinswaps
|
||||||
$ docker run -e XMR_RPC_HOST="node.xmr.to" -e BASE_XMR_RPC_PORT=18081 -t --name swap_prepare -v $SWAP_DATADIR:/coindata i_swapclient \
|
$ docker run -e XMR_RPC_HOST="node.xmr.to" -e BASE_XMR_RPC_PORT=18081 -t --name swap_prepare -v $COINDATA_PATH:/coindata i_swapclient \
|
||||||
basicswap-prepare --datadir=/coindata --withcoins=monero --withoutcoins=litecoin --htmlhost="0.0.0.0" --xmrrestoreheight=2245107
|
basicswap-prepare --datadir=/coindata --withcoins=monero --withoutcoins=litecoin --htmlhost="0.0.0.0" --xmrrestoreheight=2245107
|
||||||
|
|
||||||
Record the mnemonic from the output of the above command.
|
Record the mnemonic from the output of the above command.
|
||||||
@ -33,11 +34,19 @@ Remove swap_prepare container (and logs):
|
|||||||
|
|
||||||
Start the container
|
Start the container
|
||||||
|
|
||||||
$ export SWAP_DATADIR=/var/data/coinswaps
|
$ export COINDATA_PATH=/var/data/coinswaps
|
||||||
$ docker-compose up
|
$ docker-compose up
|
||||||
|
|
||||||
Open in browser: `http://localhost:12700`
|
Open in browser: `http://localhost:12700`
|
||||||
|
|
||||||
|
### Add a coin
|
||||||
|
|
||||||
|
$ docker-compose stop
|
||||||
|
$ export COINDATA_PATH=/var/data/coinswaps
|
||||||
|
$ docker run -t --name swap_prepare -v $COINDATA_PATH:/coindata i_swapclient basicswap-prepare --datadir=/coindata --addcoin=bitcoin
|
||||||
|
|
||||||
|
You can copy an existing pruned datadir (excluding bitcoin.conf and any wallets) over to `$COINDATA_PATH/bitcoin`
|
||||||
|
|
||||||
|
|
||||||
## Run Without Docker:
|
## Run Without Docker:
|
||||||
|
|
||||||
|
@ -11,9 +11,8 @@ Features still required (of many):
|
|||||||
- Option to lookup data from public explorers / nodes.
|
- Option to lookup data from public explorers / nodes.
|
||||||
- Ability to swap coin-types without running nodes for all coin-types
|
- Ability to swap coin-types without running nodes for all coin-types
|
||||||
- More swap protocols
|
- More swap protocols
|
||||||
- Method to load mnemonic into Particl.
|
- Manual method to set wallet seeds from particl mnemonic
|
||||||
- Load seeds for other wallets from same mnemonic.
|
- prepare script tries to load seeds automatically, btc versions < 0.21 require a fully synced chain
|
||||||
- COIN must be defined per coin.
|
|
||||||
|
|
||||||
|
|
||||||
## Seller first protocol:
|
## Seller first protocol:
|
||||||
|
1
docker/coindata/.gitignore
vendored
1
docker/coindata/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
*
|
|
@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"zmqhost": "tcp://127.0.0.1",
|
|
||||||
"zmqport": 20792,
|
|
||||||
"htmlhost": "0.0.0.0",
|
|
||||||
"htmlport": 12700,
|
|
||||||
"network_key": "7sW2UEcHXvuqEjkpE5mD584zRaQYs6WXYohue4jLFZPTvMSxwvgs",
|
|
||||||
"network_pubkey": "035758c4a22d7dd59165db02a56156e790224361eb3191f02197addcb3bde903d2",
|
|
||||||
"chainclients": {
|
|
||||||
"particl": {
|
|
||||||
"connection_type": "rpc",
|
|
||||||
"manage_daemon": true,
|
|
||||||
"rpcport": 19792,
|
|
||||||
"datadir": "/coindata/particl",
|
|
||||||
"bindir": "/opt/bin/particl",
|
|
||||||
"blocks_confirmed": 2
|
|
||||||
},
|
|
||||||
"litecoin": {
|
|
||||||
"connection_type": "rpc",
|
|
||||||
"manage_daemon": true,
|
|
||||||
"rpcport": 19795,
|
|
||||||
"datadir": "/coindata/litecoin",
|
|
||||||
"bindir": "/opt/bin/litecoin",
|
|
||||||
"use_segwit": true,
|
|
||||||
"blocks_confirmed": 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"check_progress_seconds": 60,
|
|
||||||
"check_watched_seconds": 60,
|
|
||||||
"check_expired_seconds": 60
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
prune=1000
|
|
||||||
rpcport=19795
|
|
||||||
printtoconsole=0
|
|
@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
[main]
|
|
||||||
port=14792
|
|
||||||
rpcport=19792
|
|
||||||
daemon=0
|
|
||||||
printtoconsole=0
|
|
||||||
server=1
|
|
||||||
#debug=1
|
|
||||||
debugexclude=libevent
|
|
||||||
zmqpubsmsg=tcp://127.0.0.1:20792
|
|
||||||
acceptnonstdtxn=0
|
|
||||||
spentindex=1
|
|
||||||
txindex=1
|
|
||||||
|
|
||||||
# Load a random initial wallet master key
|
|
||||||
createdefaultmasterkey=1
|
|
@ -1,3 +0,0 @@
|
|||||||
set HTML_PORT=12700:12700
|
|
||||||
docker-compose build
|
|
||||||
pause
|
|
@ -1,3 +0,0 @@
|
|||||||
set HTML_PORT=12700:12700
|
|
||||||
docker-compose up
|
|
||||||
pause
|
|
@ -538,6 +538,16 @@ class Test(unittest.TestCase):
|
|||||||
return
|
return
|
||||||
raise ValueError('wait_for_bid timed out.')
|
raise ValueError('wait_for_bid timed out.')
|
||||||
|
|
||||||
|
def wait_for_none_active(self, port, wait_for=30):
|
||||||
|
for i in range(wait_for):
|
||||||
|
if stop_test:
|
||||||
|
raise ValueError('Test stopped.')
|
||||||
|
time.sleep(1)
|
||||||
|
js = json.loads(urlopen('http://localhost:{}/json'.format(port)).read())
|
||||||
|
if js['num_swapping'] == 0 and js['num_watched_outputs'] == 0:
|
||||||
|
return
|
||||||
|
raise ValueError('wait_for_none_active timed out.')
|
||||||
|
|
||||||
def delay_for(self, delay_for=60):
|
def delay_for(self, delay_for=60):
|
||||||
logging.info('Delaying for {} seconds.'.format(delay_for))
|
logging.info('Delaying for {} seconds.'.format(delay_for))
|
||||||
delay_event.clear()
|
delay_event.clear()
|
||||||
@ -632,6 +642,9 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
js_w0_after = json.loads(urlopen('http://localhost:1800/json/wallets').read())
|
js_w0_after = json.loads(urlopen('http://localhost:1800/json/wallets').read())
|
||||||
|
|
||||||
|
self.wait_for_none_active(1800)
|
||||||
|
self.wait_for_none_active(1801)
|
||||||
|
|
||||||
def test_04_follower_recover_b_lock_tx(self):
|
def test_04_follower_recover_b_lock_tx(self):
|
||||||
logging.info('---------- Test PART to XMR follower recovers coin b lock tx')
|
logging.info('---------- Test PART to XMR follower recovers coin b lock tx')
|
||||||
|
|
||||||
@ -709,12 +722,15 @@ class Test(unittest.TestCase):
|
|||||||
self.wait_for_bid(swap_clients[0], bid1_id, BidStates.SWAP_COMPLETED, wait_for=180)
|
self.wait_for_bid(swap_clients[0], bid1_id, BidStates.SWAP_COMPLETED, wait_for=180)
|
||||||
self.wait_for_bid(swap_clients[1], bid1_id, BidStates.SWAP_COMPLETED, sent=True)
|
self.wait_for_bid(swap_clients[1], bid1_id, BidStates.SWAP_COMPLETED, sent=True)
|
||||||
|
|
||||||
self.wait_for_bid(swap_clients[0], bid2_id, BidStates.SWAP_COMPLETED, wait_for=60)
|
self.wait_for_bid(swap_clients[0], bid2_id, BidStates.SWAP_COMPLETED, wait_for=120)
|
||||||
self.wait_for_bid(swap_clients[1], bid2_id, BidStates.SWAP_COMPLETED, sent=True)
|
self.wait_for_bid(swap_clients[1], bid2_id, BidStates.SWAP_COMPLETED, sent=True)
|
||||||
|
|
||||||
self.wait_for_bid(swap_clients[0], bid3_id, BidStates.SWAP_COMPLETED, wait_for=60)
|
self.wait_for_bid(swap_clients[0], bid3_id, BidStates.SWAP_COMPLETED, wait_for=120)
|
||||||
self.wait_for_bid(swap_clients[1], bid3_id, BidStates.SWAP_COMPLETED, sent=True)
|
self.wait_for_bid(swap_clients[1], bid3_id, BidStates.SWAP_COMPLETED, sent=True)
|
||||||
|
|
||||||
|
self.wait_for_none_active(1800)
|
||||||
|
self.wait_for_none_active(1801)
|
||||||
|
|
||||||
def test_07_revoke_offer(self):
|
def test_07_revoke_offer(self):
|
||||||
logging.info('---------- Test offer revocaction')
|
logging.info('---------- Test offer revocaction')
|
||||||
swap_clients = self.swap_clients
|
swap_clients = self.swap_clients
|
||||||
@ -733,6 +749,17 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
swap_clients[1].withdrawCoin(Coins.XMR, 1.1, address_to, False)
|
swap_clients[1].withdrawCoin(Coins.XMR, 1.1, address_to, False)
|
||||||
|
|
||||||
|
def test_09_auto_accept(self):
|
||||||
|
logging.info('---------- Test BTC to XMR auto accept')
|
||||||
|
swap_clients = self.swap_clients
|
||||||
|
offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.XMR, 11 * COIN, 101 * XMR_COIN, 10 * COIN, SwapTypes.XMR_SWAP, auto_accept_bids=True)
|
||||||
|
self.wait_for_offer(swap_clients[1], offer_id)
|
||||||
|
offer = swap_clients[1].listOffers(filters={'offer_id': offer_id})[0]
|
||||||
|
|
||||||
|
bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from)
|
||||||
|
self.wait_for_bid(swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180)
|
||||||
|
self.wait_for_bid(swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
Reference in New Issue
Block a user