mirror of
https://github.com/basicswap/basicswap.git
synced 2025-01-18 16:44:34 +00:00
Add txn 1st seen height to bid.
Format balance amount. Start prepare script.
This commit is contained in:
parent
f1efb0a317
commit
76af4a941e
12 changed files with 378 additions and 201 deletions
|
@ -39,7 +39,7 @@ jobs:
|
||||||
- travis_retry pip install codespell==1.15.0
|
- travis_retry pip install codespell==1.15.0
|
||||||
before_script:
|
before_script:
|
||||||
script:
|
script:
|
||||||
- PYTHONWARNINGS="ignore" flake8 --ignore=E501,F841 --exclude=key.py,messages_pb2.py
|
- PYTHONWARNINGS="ignore" flake8 --ignore=E501,F841 --exclude=key.py,messages_pb2.py,.eggs
|
||||||
- codespell --check-filenames --disable-colors --quiet-level=7 -S .git
|
- codespell --check-filenames --disable-colors --quiet-level=7 -S .git,.eggs
|
||||||
after_success:
|
after_success:
|
||||||
- echo "End lint"
|
- echo "End lint"
|
||||||
|
|
|
@ -24,6 +24,7 @@ Features still required (of many):
|
||||||
- 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.
|
- Method to load mnemonic into Particl.
|
||||||
|
- COIN must be defined per coin.
|
||||||
|
|
||||||
|
|
||||||
## Seller first protocol:
|
## Seller first protocol:
|
||||||
|
|
|
@ -351,6 +351,7 @@ class Bid(Base):
|
||||||
initiate_spend_txid = sa.Column(sa.LargeBinary)
|
initiate_spend_txid = sa.Column(sa.LargeBinary)
|
||||||
initiate_spend_n = sa.Column(sa.Integer)
|
initiate_spend_n = sa.Column(sa.Integer)
|
||||||
|
|
||||||
|
initiate_txn_height = sa.Column(sa.Integer)
|
||||||
initiate_txn_state = sa.Column(sa.Integer)
|
initiate_txn_state = sa.Column(sa.Integer)
|
||||||
initiate_txn_states = sa.Column(sa.LargeBinary) # Packed states and times
|
initiate_txn_states = sa.Column(sa.LargeBinary) # Packed states and times
|
||||||
|
|
||||||
|
@ -364,6 +365,7 @@ class Bid(Base):
|
||||||
participate_spend_txid = sa.Column(sa.LargeBinary)
|
participate_spend_txid = sa.Column(sa.LargeBinary)
|
||||||
participate_spend_n = sa.Column(sa.Integer)
|
participate_spend_n = sa.Column(sa.Integer)
|
||||||
|
|
||||||
|
participate_txn_height = sa.Column(sa.Integer)
|
||||||
participate_txn_state = sa.Column(sa.Integer)
|
participate_txn_state = sa.Column(sa.Integer)
|
||||||
participate_txn_states = sa.Column(sa.LargeBinary) # Packed states and times
|
participate_txn_states = sa.Column(sa.LargeBinary) # Packed states and times
|
||||||
|
|
||||||
|
@ -371,6 +373,8 @@ class Bid(Base):
|
||||||
state_time = sa.Column(sa.BigInteger) # timestamp of last state change
|
state_time = sa.Column(sa.BigInteger) # timestamp of last state change
|
||||||
states = sa.Column(sa.LargeBinary) # Packed states and times
|
states = sa.Column(sa.LargeBinary) # Packed states and times
|
||||||
|
|
||||||
|
state_note = sa.Column(sa.String)
|
||||||
|
|
||||||
def setITXState(self, new_state):
|
def setITXState(self, new_state):
|
||||||
self.initiate_txn_state = new_state
|
self.initiate_txn_state = new_state
|
||||||
if self.initiate_txn_states is None:
|
if self.initiate_txn_states is None:
|
||||||
|
@ -1438,6 +1442,8 @@ class BasicSwap():
|
||||||
self.coin_clients[coin_type]['last_height_checked'] = tx_height
|
self.coin_clients[coin_type]['last_height_checked'] = tx_height
|
||||||
self.log.debug('Rewind checking of %s chain to height %d', chain_name, tx_height)
|
self.log.debug('Rewind checking of %s chain to height %d', chain_name, tx_height)
|
||||||
|
|
||||||
|
return tx_height
|
||||||
|
|
||||||
def addParticipateTxn(self, bid_id, bid, coin_type, txid_hex, vout, tx_height):
|
def addParticipateTxn(self, bid_id, bid, coin_type, txid_hex, vout, tx_height):
|
||||||
bid.participate_txid = bytes.fromhex(txid_hex)
|
bid.participate_txid = bytes.fromhex(txid_hex)
|
||||||
bid.participate_txn_n = vout
|
bid.participate_txn_n = vout
|
||||||
|
@ -1447,7 +1453,7 @@ class BasicSwap():
|
||||||
self.log.debug('Watching %s chain for spend of output %s %d', chain_name, txid_hex, vout)
|
self.log.debug('Watching %s chain for spend of output %s %d', chain_name, txid_hex, vout)
|
||||||
|
|
||||||
# TODO: Check connection type
|
# TODO: Check connection type
|
||||||
self.setLastHeightChecked(coin_type, tx_height)
|
bid.participate_txn_height = self.setLastHeightChecked(coin_type, tx_height)
|
||||||
|
|
||||||
self.log.debug('Adding watched output %s bid %s tx %s type %s', coin_type, bid_id.hex(), txid_hex, BidStates.SWAP_PARTICIPATING)
|
self.log.debug('Adding watched output %s bid %s tx %s type %s', coin_type, bid_id.hex(), txid_hex, BidStates.SWAP_PARTICIPATING)
|
||||||
self.coin_clients[coin_type]['watched_outputs'].append((bid_id, txid_hex, vout, BidStates.SWAP_PARTICIPATING))
|
self.coin_clients[coin_type]['watched_outputs'].append((bid_id, txid_hex, vout, BidStates.SWAP_PARTICIPATING))
|
||||||
|
@ -1556,7 +1562,7 @@ class BasicSwap():
|
||||||
if bid.initiate_txn_n is None:
|
if bid.initiate_txn_n is None:
|
||||||
bid.initiate_txn_n = index
|
bid.initiate_txn_n = index
|
||||||
# Start checking for spends of initiate_txn before fully confirmed
|
# Start checking for spends of initiate_txn before fully confirmed
|
||||||
self.setLastHeightChecked(coin_from, tx_height)
|
bid.initiate_txn_height = self.setLastHeightChecked(coin_from, tx_height)
|
||||||
self.log.debug('Adding watched output %s bid %s tx %s type %s', coin_from, bid_id.hex(), initiate_txnid_hex, BidStates.SWAP_INITIATED)
|
self.log.debug('Adding watched output %s bid %s tx %s type %s', coin_from, bid_id.hex(), initiate_txnid_hex, BidStates.SWAP_INITIATED)
|
||||||
self.coin_clients[coin_from]['watched_outputs'].append((bid_id, initiate_txnid_hex, bid.initiate_txn_n, BidStates.SWAP_INITIATED))
|
self.coin_clients[coin_from]['watched_outputs'].append((bid_id, initiate_txnid_hex, bid.initiate_txn_n, BidStates.SWAP_INITIATED))
|
||||||
if bid.initiate_txn_state is None or bid.initiate_txn_state < TxStates.TX_SENT:
|
if bid.initiate_txn_state is None or bid.initiate_txn_state < TxStates.TX_SENT:
|
||||||
|
@ -2112,7 +2118,7 @@ class BasicSwap():
|
||||||
'deposit_address': self.getCachedAddressForCoin(coin),
|
'deposit_address': self.getCachedAddressForCoin(coin),
|
||||||
'name': chainparams[coin]['name'].capitalize(),
|
'name': chainparams[coin]['name'].capitalize(),
|
||||||
'blocks': blockchaininfo['blocks'],
|
'blocks': blockchaininfo['blocks'],
|
||||||
'balance': walletinfo.get('total_balance', walletinfo['balance']),
|
'balance': format8(walletinfo.get('total_balance', walletinfo['balance']) * COIN),
|
||||||
'synced': '{0:.2f}'.format(round(blockchaininfo['verificationprogress'], 2)),
|
'synced': '{0:.2f}'.format(round(blockchaininfo['verificationprogress'], 2)),
|
||||||
}
|
}
|
||||||
return rv
|
return rv
|
||||||
|
|
|
@ -9,16 +9,16 @@ import os
|
||||||
DATADIRS = os.path.expanduser(os.getenv('DATADIRS', '/tmp/basicswap'))
|
DATADIRS = os.path.expanduser(os.getenv('DATADIRS', '/tmp/basicswap'))
|
||||||
|
|
||||||
PARTICL_BINDIR = os.path.expanduser(os.getenv('PARTICL_BINDIR', ''))
|
PARTICL_BINDIR = os.path.expanduser(os.getenv('PARTICL_BINDIR', ''))
|
||||||
PARTICLD = os.getenv('PARTICLD', 'particld')
|
PARTICLD = os.getenv('PARTICLD', 'particld' + ('.exe' if os.name == 'nt' else ''))
|
||||||
PARTICL_CLI = os.getenv('PARTICL_CLI', 'particl-cli')
|
PARTICL_CLI = os.getenv('PARTICL_CLI', 'particl-cli' + ('.exe' if os.name == 'nt' else ''))
|
||||||
PARTICL_TX = os.getenv('PARTICL_TX', 'particl-tx')
|
PARTICL_TX = os.getenv('PARTICL_TX', 'particl-tx' + ('.exe' if os.name == 'nt' else ''))
|
||||||
|
|
||||||
BITCOIN_BINDIR = os.path.expanduser(os.getenv('BITCOIN_BINDIR', ''))
|
BITCOIN_BINDIR = os.path.expanduser(os.getenv('BITCOIN_BINDIR', ''))
|
||||||
BITCOIND = os.getenv('BITCOIND', 'bitcoind')
|
BITCOIND = os.getenv('BITCOIND', 'bitcoind' + ('.exe' if os.name == 'nt' else ''))
|
||||||
BITCOIN_CLI = os.getenv('BITCOIN_CLI', 'bitcoin-cli')
|
BITCOIN_CLI = os.getenv('BITCOIN_CLI', 'bitcoin-cli' + ('.exe' if os.name == 'nt' else ''))
|
||||||
BITCOIN_TX = os.getenv('BITCOIN_TX', 'bitcoin-tx')
|
BITCOIN_TX = os.getenv('BITCOIN_TX', 'bitcoin-tx' + ('.exe' if os.name == 'nt' else ''))
|
||||||
|
|
||||||
LITECOIN_BINDIR = os.path.expanduser(os.getenv('LITECOIN_BINDIR', ''))
|
LITECOIN_BINDIR = os.path.expanduser(os.getenv('LITECOIN_BINDIR', ''))
|
||||||
LITECOIND = os.getenv('LITECOIND', 'litecoind')
|
LITECOIND = os.getenv('LITECOIND', 'litecoind' + ('.exe' if os.name == 'nt' else ''))
|
||||||
LITECOIN_CLI = os.getenv('LITECOIN_CLI', 'litecoin-cli')
|
LITECOIN_CLI = os.getenv('LITECOIN_CLI', 'litecoin-cli' + ('.exe' if os.name == 'nt' else ''))
|
||||||
LITECOIN_TX = os.getenv('LITECOIN_TX', 'litecoin-tx')
|
LITECOIN_TX = os.getenv('LITECOIN_TX', 'litecoin-tx' + ('.exe' if os.name == 'nt' else ''))
|
||||||
|
|
|
@ -130,7 +130,7 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||||
cid = str(int(k))
|
cid = str(int(k))
|
||||||
content += '<h4>' + w['name'] + '</h4>' \
|
content += '<h4>' + w['name'] + '</h4>' \
|
||||||
+ '<table>' \
|
+ '<table>' \
|
||||||
+ '<tr><td>Balance:</td><td>' + str(w['balance']) + '</td></tr>' \
|
+ '<tr><td>Balance:</td><td>' + w['balance'] + '</td></tr>' \
|
||||||
+ '<tr><td>Blocks:</td><td>' + str(w['blocks']) + '</td></tr>' \
|
+ '<tr><td>Blocks:</td><td>' + str(w['blocks']) + '</td></tr>' \
|
||||||
+ '<tr><td>Synced:</td><td>' + str(w['synced']) + '</td></tr>' \
|
+ '<tr><td>Synced:</td><td>' + str(w['synced']) + '</td></tr>' \
|
||||||
+ '<tr><td><input type="submit" name="newaddr_' + cid + '" value="Deposit Address"></td><td>' + str(w['deposit_address']) + '</td></tr>' \
|
+ '<tr><td><input type="submit" name="newaddr_' + cid + '" value="Deposit Address"></td><td>' + str(w['deposit_address']) + '</td></tr>' \
|
||||||
|
|
117
bin/basicswap-prepare.py
Normal file
117
bin/basicswap-prepare.py
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright (c) 2019 tecnovert
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Particl Atomic Swap - Proof of Concept
|
||||||
|
|
||||||
|
sudo pip install python-gnupg
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import hashlib
|
||||||
|
import mmap
|
||||||
|
import tarfile
|
||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import gnupg
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
logger.level = logging.DEBUG
|
||||||
|
|
||||||
|
|
||||||
|
def printVersion():
|
||||||
|
from basicswap import __version__
|
||||||
|
logger.info('Basicswap version:', __version__)
|
||||||
|
|
||||||
|
|
||||||
|
def printHelp():
|
||||||
|
logger.info('Usage: basicswap-prepare ')
|
||||||
|
logger.info('\n--help, -h Print help.')
|
||||||
|
logger.info('\n--version, -v Print version.')
|
||||||
|
logger.info('\n--datadir=PATH Path to basicswap data directory, default:~/.basicswap.')
|
||||||
|
logger.info('\n--mainnet Run in mainnet mode.')
|
||||||
|
logger.info('\n--testnet Run in testnet mode.')
|
||||||
|
logger.info('\n--regtest Run in regtest mode.')
|
||||||
|
logger.info('\n--particl_mnemonic= Recovery phrase to use for the Particl wallet, default is randomly generated.')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print('main')
|
||||||
|
data_dir = None
|
||||||
|
chain = 'mainnet'
|
||||||
|
particl_wallet_mnemonic = None
|
||||||
|
|
||||||
|
for v in sys.argv[1:]:
|
||||||
|
if len(v) < 2 or v[0] != '-':
|
||||||
|
logger.warning('Unknown argument', v)
|
||||||
|
continue
|
||||||
|
|
||||||
|
s = v.split('=')
|
||||||
|
name = s[0].strip()
|
||||||
|
|
||||||
|
for i in range(2):
|
||||||
|
if name[0] == '-':
|
||||||
|
name = name[1:]
|
||||||
|
|
||||||
|
if name == 'v' or name == 'version':
|
||||||
|
printVersion()
|
||||||
|
return 0
|
||||||
|
if name == 'h' or name == 'help':
|
||||||
|
printHelp()
|
||||||
|
return 0
|
||||||
|
if name == 'mainnet':
|
||||||
|
continue
|
||||||
|
if name == 'testnet':
|
||||||
|
chain = 'testnet'
|
||||||
|
continue
|
||||||
|
if name == 'regtest':
|
||||||
|
chain = 'regtest'
|
||||||
|
continue
|
||||||
|
|
||||||
|
if len(s) == 2:
|
||||||
|
if name == 'datadir':
|
||||||
|
data_dir = os.path.expanduser(s[1])
|
||||||
|
continue
|
||||||
|
if name == 'particl_mnemonic':
|
||||||
|
particl_wallet_mnemonic = s[1]
|
||||||
|
continue
|
||||||
|
|
||||||
|
logger.warning('Unknown argument', v)
|
||||||
|
|
||||||
|
if data_dir is None:
|
||||||
|
default_datadir = '~/.basicswap'
|
||||||
|
data_dir = os.path.join(os.path.expanduser(default_datadir))
|
||||||
|
logger.info('Using datadir: %s', data_dir)
|
||||||
|
logger.info('Chain: %s', chain)
|
||||||
|
|
||||||
|
if not os.path.exists(data_dir):
|
||||||
|
os.makedirs(data_dir)
|
||||||
|
|
||||||
|
config_path = os.path.join(data_dir, 'basicswap.json')
|
||||||
|
if os.path.exists(config_path):
|
||||||
|
sys.stderr.write('Error: {} exists, exiting.'.format(config_path))
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
'debug': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
with open(config_path, 'w') as fp:
|
||||||
|
json.dump(settings, fp, indent=4)
|
||||||
|
|
||||||
|
logger.info('Done.')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -1 +0,0 @@
|
||||||
basicswap_run.py
|
|
181
bin/basicswap-run.py
Normal file
181
bin/basicswap-run.py
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright (c) 2019 tecnovert
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Particl Atomic Swap - Proof of Concept
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
$ pacman -S python-pyzmq python-plyvel protobuf
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import traceback
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import basicswap.config as cfg
|
||||||
|
from basicswap import __version__
|
||||||
|
from basicswap.basicswap import BasicSwap
|
||||||
|
from basicswap.http_server import HttpThread
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
logger.level = logging.DEBUG
|
||||||
|
logger.addHandler(logging.StreamHandler(sys.stdout))
|
||||||
|
|
||||||
|
ALLOW_CORS = False
|
||||||
|
swap_client = None
|
||||||
|
|
||||||
|
|
||||||
|
def signal_handler(sig, frame):
|
||||||
|
logger.info('Signal %d detected, ending program.' % (sig))
|
||||||
|
if swap_client is not None:
|
||||||
|
swap_client.stopRunning()
|
||||||
|
|
||||||
|
|
||||||
|
def startDaemon(node_dir, bin_dir, daemon_bin):
|
||||||
|
daemon_bin = os.path.join(bin_dir, daemon_bin)
|
||||||
|
|
||||||
|
args = [daemon_bin, '-datadir=' + node_dir]
|
||||||
|
logger.info('Starting node ' + daemon_bin + ' ' + '-datadir=' + node_dir)
|
||||||
|
return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
|
||||||
|
|
||||||
|
def runClient(fp, dataDir, chain):
|
||||||
|
global swap_client
|
||||||
|
settings_path = os.path.join(dataDir, 'basicswap.json')
|
||||||
|
|
||||||
|
if not os.path.exists(settings_path):
|
||||||
|
raise ValueError('Settings file not found: ' + str(settings_path))
|
||||||
|
|
||||||
|
with open(settings_path) as fs:
|
||||||
|
settings = json.load(fs)
|
||||||
|
|
||||||
|
daemons = []
|
||||||
|
|
||||||
|
for c, v in settings['chainclients'].items():
|
||||||
|
if v['manage_daemon'] is True:
|
||||||
|
logger.info('Starting {} daemon'.format(c.capitalize()))
|
||||||
|
if c == 'particl':
|
||||||
|
daemons.append(startDaemon(v['datadir'], cfg.PARTICL_BINDIR, cfg.PARTICLD))
|
||||||
|
logger.info('Started {} {}'.format(cfg.PARTICLD, daemons[-1].pid))
|
||||||
|
elif c == 'bitcoin':
|
||||||
|
daemons.append(startDaemon(v['datadir'], cfg.BITCOIN_BINDIR, cfg.BITCOIND))
|
||||||
|
logger.info('Started {} {}'.format(cfg.BITCOIND, daemons[-1].pid))
|
||||||
|
elif c == 'litecoin':
|
||||||
|
daemons.append(startDaemon(v['datadir'], cfg.LITECOIN_BINDIR, cfg.LITECOIND))
|
||||||
|
logger.info('Started {} {}'.format(cfg.LITECOIND, daemons[-1].pid))
|
||||||
|
else:
|
||||||
|
logger.warning('Unknown chain', c)
|
||||||
|
|
||||||
|
swap_client = BasicSwap(fp, dataDir, settings, chain)
|
||||||
|
|
||||||
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
signal.signal(signal.SIGTERM, signal_handler)
|
||||||
|
swap_client.start()
|
||||||
|
|
||||||
|
threads = []
|
||||||
|
if 'htmlhost' in settings:
|
||||||
|
swap_client.log.info('Starting server at %s:%d.' % (settings['htmlhost'], settings['htmlport']))
|
||||||
|
allow_cors = settings['allowcors'] if 'allowcors' in settings else ALLOW_CORS
|
||||||
|
tS1 = HttpThread(fp, settings['htmlhost'], settings['htmlport'], allow_cors, swap_client)
|
||||||
|
threads.append(tS1)
|
||||||
|
tS1.start()
|
||||||
|
|
||||||
|
try:
|
||||||
|
logger.info('Exit with Ctrl + c.')
|
||||||
|
while swap_client.is_running:
|
||||||
|
time.sleep(0.5)
|
||||||
|
swap_client.update()
|
||||||
|
except Exception:
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
swap_client.log.info('Stopping threads.')
|
||||||
|
for t in threads:
|
||||||
|
t.stop()
|
||||||
|
t.join()
|
||||||
|
|
||||||
|
for d in daemons:
|
||||||
|
logger.info('Terminating {}'.format(d.pid))
|
||||||
|
d.terminate()
|
||||||
|
d.wait(timeout=120)
|
||||||
|
if d.stdout:
|
||||||
|
d.stdout.close()
|
||||||
|
if d.stderr:
|
||||||
|
d.stderr.close()
|
||||||
|
if d.stdin:
|
||||||
|
d.stdin.close()
|
||||||
|
|
||||||
|
|
||||||
|
def printVersion():
|
||||||
|
logger.info('Basicswap version:', __version__)
|
||||||
|
|
||||||
|
|
||||||
|
def printHelp():
|
||||||
|
logger.info('basicswap-run.py --datadir=path -testnet')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
data_dir = None
|
||||||
|
chain = 'mainnet'
|
||||||
|
|
||||||
|
for v in sys.argv[1:]:
|
||||||
|
if len(v) < 2 or v[0] != '-':
|
||||||
|
logger.warning('Unknown argument', v)
|
||||||
|
continue
|
||||||
|
|
||||||
|
s = v.split('=')
|
||||||
|
name = s[0].strip()
|
||||||
|
|
||||||
|
for i in range(2):
|
||||||
|
if name[0] == '-':
|
||||||
|
name = name[1:]
|
||||||
|
|
||||||
|
if name == 'v' or name == 'version':
|
||||||
|
printVersion()
|
||||||
|
return 0
|
||||||
|
if name == 'h' or name == 'help':
|
||||||
|
printHelp()
|
||||||
|
return 0
|
||||||
|
if name == 'testnet':
|
||||||
|
chain = 'testnet'
|
||||||
|
continue
|
||||||
|
if name == 'regtest':
|
||||||
|
chain = 'regtest'
|
||||||
|
continue
|
||||||
|
|
||||||
|
if len(s) == 2:
|
||||||
|
if name == 'datadir':
|
||||||
|
data_dir = os.path.expanduser(s[1])
|
||||||
|
continue
|
||||||
|
|
||||||
|
logger.warning('Unknown argument', v)
|
||||||
|
|
||||||
|
if data_dir is None:
|
||||||
|
default_datadir = '~/.basicswap'
|
||||||
|
data_dir = os.path.join(os.path.expanduser(default_datadir))
|
||||||
|
logger.info('Using datadir:', data_dir)
|
||||||
|
logger.info('Chain:', chain)
|
||||||
|
|
||||||
|
if not os.path.exists(data_dir):
|
||||||
|
os.makedirs(data_dir)
|
||||||
|
|
||||||
|
with open(os.path.join(data_dir, 'basicswap.log'), 'a') as fp:
|
||||||
|
logger.info(os.path.basename(sys.argv[0]) + ', version: ' + __version__ + '\n\n')
|
||||||
|
runClient(fp, data_dir, chain)
|
||||||
|
|
||||||
|
logger.info('Done.')
|
||||||
|
return swap_client.fail_code if swap_client is not None else 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -1,182 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright (c) 2019 tecnovert
|
|
||||||
# Distributed under the MIT software license, see the accompanying
|
|
||||||
# file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Particl Atomic Swap - Proof of Concept
|
|
||||||
|
|
||||||
Dependencies:
|
|
||||||
$ pacman -S python-pyzmq python-plyvel protobuf
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import json
|
|
||||||
import traceback
|
|
||||||
import signal
|
|
||||||
import subprocess
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import basicswap.config as cfg
|
|
||||||
from basicswap import __version__
|
|
||||||
from basicswap.basicswap import BasicSwap
|
|
||||||
from basicswap.http_server import HttpThread
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
|
||||||
logger.level = logging.DEBUG
|
|
||||||
logger.addHandler(logging.StreamHandler(sys.stdout))
|
|
||||||
|
|
||||||
ALLOW_CORS = False
|
|
||||||
swap_client = None
|
|
||||||
|
|
||||||
|
|
||||||
def signal_handler(sig, frame):
|
|
||||||
logger.info('Signal %d detected, ending program.' % (sig))
|
|
||||||
if swap_client is not None:
|
|
||||||
swap_client.stopRunning()
|
|
||||||
|
|
||||||
|
|
||||||
def startDaemon(node_dir, bin_dir, daemon_bin):
|
|
||||||
daemon_bin = os.path.join(bin_dir, daemon_bin)
|
|
||||||
|
|
||||||
args = [daemon_bin, '-datadir=' + node_dir]
|
|
||||||
logger.info('Starting node ' + daemon_bin + ' ' + '-datadir=' + node_dir)
|
|
||||||
return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
||||||
|
|
||||||
|
|
||||||
def runClient(fp, dataDir, chain):
|
|
||||||
global swap_client
|
|
||||||
settings_path = os.path.join(dataDir, 'basicswap.json')
|
|
||||||
|
|
||||||
if not os.path.exists(settings_path):
|
|
||||||
raise ValueError('Settings file not found: ' + str(settings_path))
|
|
||||||
|
|
||||||
with open(settings_path) as fs:
|
|
||||||
settings = json.load(fs)
|
|
||||||
|
|
||||||
daemons = []
|
|
||||||
|
|
||||||
for c, v in settings['chainclients'].items():
|
|
||||||
if v['manage_daemon'] is True:
|
|
||||||
logger.info('Starting {} daemon'.format(c.capitalize()))
|
|
||||||
if c == 'particl':
|
|
||||||
daemons.append(startDaemon(v['datadir'], cfg.PARTICL_BINDIR, cfg.PARTICLD))
|
|
||||||
logger.info('Started {} {}'.format(cfg.PARTICLD, daemons[-1].pid))
|
|
||||||
elif c == 'bitcoin':
|
|
||||||
daemons.append(startDaemon(v['datadir'], cfg.BITCOIN_BINDIR, cfg.BITCOIND))
|
|
||||||
logger.info('Started {} {}'.format(cfg.BITCOIND, daemons[-1].pid))
|
|
||||||
elif c == 'litecoin':
|
|
||||||
daemons.append(startDaemon(v['datadir'], cfg.LITECOIN_BINDIR, cfg.LITECOIND))
|
|
||||||
logger.info('Started {} {}'.format(cfg.LITECOIND, daemons[-1].pid))
|
|
||||||
else:
|
|
||||||
logger.warning('Unknown chain', c)
|
|
||||||
|
|
||||||
swap_client = BasicSwap(fp, dataDir, settings, chain)
|
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
|
||||||
signal.signal(signal.SIGTERM, signal_handler)
|
|
||||||
swap_client.start()
|
|
||||||
|
|
||||||
threads = []
|
|
||||||
if 'htmlhost' in settings:
|
|
||||||
swap_client.log.info('Starting server at %s:%d.' % (settings['htmlhost'], settings['htmlport']))
|
|
||||||
allow_cors = settings['allowcors'] if 'allowcors' in settings else ALLOW_CORS
|
|
||||||
tS1 = HttpThread(fp, settings['htmlhost'], settings['htmlport'], allow_cors, swap_client)
|
|
||||||
threads.append(tS1)
|
|
||||||
tS1.start()
|
|
||||||
|
|
||||||
try:
|
|
||||||
logger.info('Exit with Ctrl + c.')
|
|
||||||
while swap_client.is_running:
|
|
||||||
time.sleep(0.5)
|
|
||||||
swap_client.update()
|
|
||||||
except Exception:
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
swap_client.log.info('Stopping threads.')
|
|
||||||
for t in threads:
|
|
||||||
t.stop()
|
|
||||||
t.join()
|
|
||||||
|
|
||||||
for d in daemons:
|
|
||||||
logger.info('Terminating {}'.format(d.pid))
|
|
||||||
d.terminate()
|
|
||||||
d.wait(timeout=120)
|
|
||||||
if d.stdout:
|
|
||||||
d.stdout.close()
|
|
||||||
if d.stderr:
|
|
||||||
d.stderr.close()
|
|
||||||
if d.stdin:
|
|
||||||
d.stdin.close()
|
|
||||||
|
|
||||||
|
|
||||||
def printVersion():
|
|
||||||
logger.info('Basicswap version:', __version__)
|
|
||||||
|
|
||||||
|
|
||||||
def printHelp():
|
|
||||||
logger.info('basicswap-run.py --datadir=path -testnet')
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
data_dir = None
|
|
||||||
chain = 'mainnet'
|
|
||||||
|
|
||||||
for v in sys.argv[1:]:
|
|
||||||
if len(v) < 2 or v[0] != '-':
|
|
||||||
logger.warning('Unknown argument', v)
|
|
||||||
continue
|
|
||||||
|
|
||||||
s = v.split('=')
|
|
||||||
name = s[0].strip()
|
|
||||||
|
|
||||||
for i in range(2):
|
|
||||||
if name[0] == '-':
|
|
||||||
name = name[1:]
|
|
||||||
|
|
||||||
if name == 'v' or name == 'version':
|
|
||||||
printVersion()
|
|
||||||
return 0
|
|
||||||
if name == 'h' or name == 'help':
|
|
||||||
printHelp()
|
|
||||||
return 0
|
|
||||||
if name == 'testnet':
|
|
||||||
chain = 'testnet'
|
|
||||||
continue
|
|
||||||
if name == 'regtest':
|
|
||||||
chain = 'regtest'
|
|
||||||
continue
|
|
||||||
|
|
||||||
if len(s) == 2:
|
|
||||||
if name == 'datadir':
|
|
||||||
data_dir = os.path.expanduser(s[1])
|
|
||||||
continue
|
|
||||||
|
|
||||||
logger.warning('Unknown argument', v)
|
|
||||||
|
|
||||||
if data_dir is None:
|
|
||||||
data_dir = os.path.join(os.path.expanduser(os.path.join(cfg.DATADIRS)), 'particl', ('' if chain == 'mainnet' else chain), 'basicswap')
|
|
||||||
|
|
||||||
print('data_dir:', data_dir)
|
|
||||||
if chain != 'mainnet':
|
|
||||||
logger.info('chain:', chain)
|
|
||||||
|
|
||||||
if not os.path.exists(data_dir):
|
|
||||||
os.makedirs(data_dir)
|
|
||||||
|
|
||||||
with open(os.path.join(data_dir, 'basicswap.log'), 'a') as fp:
|
|
||||||
logger.info(os.path.basename(sys.argv[0]) + ', version: ' + __version__ + '\n\n')
|
|
||||||
runClient(fp, data_dir, chain)
|
|
||||||
|
|
||||||
logger.info('Done.')
|
|
||||||
return swap_client.fail_code if swap_client is not None else 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
1
setup.py
1
setup.py
|
@ -26,6 +26,7 @@ setuptools.setup(
|
||||||
"pyzmq",
|
"pyzmq",
|
||||||
"protobuf",
|
"protobuf",
|
||||||
"sqlalchemy",
|
"sqlalchemy",
|
||||||
|
"python-gnupg",
|
||||||
],
|
],
|
||||||
entry_points={
|
entry_points={
|
||||||
"console_scripts": [
|
"console_scripts": [
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import tests.test_run
|
|
||||||
import tests.test_other
|
import tests.test_other
|
||||||
|
import tests.test_prepare
|
||||||
|
import tests.test_run
|
||||||
|
|
||||||
|
|
||||||
def test_suite():
|
def test_suite():
|
||||||
loader = unittest.TestLoader()
|
loader = unittest.TestLoader()
|
||||||
suite = loader.loadTestsFromModule(tests.test_run)
|
|
||||||
suite.addTests(loader.loadTestsFromModule(tests.test_other))
|
suite.addTests(loader.loadTestsFromModule(tests.test_other))
|
||||||
|
suite.addTests(loader.loadTestsFromModule(tests.test_prepare))
|
||||||
|
suite = loader.loadTestsFromModule(tests.test_run)
|
||||||
|
|
||||||
return suite
|
return suite
|
||||||
|
|
51
tests/test_prepare.py
Normal file
51
tests/test_prepare.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright (c) 2019 tecnovert
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file LICENSE.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
from io import StringIO
|
||||||
|
import logging
|
||||||
|
import shutil
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
prepareSystem = importlib.import_module('bin.basicswap-prepare')
|
||||||
|
test_path = os.path.expanduser('~/test_basicswap')
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
logger.level = logging.DEBUG
|
||||||
|
|
||||||
|
|
||||||
|
class Test(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(self):
|
||||||
|
try:
|
||||||
|
shutil.rmtree(test_path)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning('tearDownClass %s', str(e))
|
||||||
|
|
||||||
|
def test_no_overwrite(self):
|
||||||
|
testargs = ['basicswap-prepare', '-datadir=' + test_path]
|
||||||
|
with patch.object(sys, 'argv', testargs):
|
||||||
|
prepareSystem.main()
|
||||||
|
|
||||||
|
self.assertTrue(os.path.exists(os.path.join(test_path, 'basicswap.json')))
|
||||||
|
|
||||||
|
testargs = ['basicswap-prepare', '-datadir=' + test_path]
|
||||||
|
with patch('sys.stderr', new=StringIO()) as fake_stderr:
|
||||||
|
with patch.object(sys, 'argv', testargs):
|
||||||
|
with self.assertRaises(SystemExit) as cm:
|
||||||
|
prepareSystem.main()
|
||||||
|
|
||||||
|
self.assertEqual(cm.exception.code, 1)
|
||||||
|
logger.info('fake_stderr.getvalue() %s', fake_stderr.getvalue())
|
||||||
|
self.assertTrue('exists, exiting' in fake_stderr.getvalue())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Loading…
Reference in a new issue