|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
# Copyright (c) 2019-2021 tecnovert
|
|
|
|
# Distributed under the MIT software license, see the accompanying
|
|
|
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
|
|
|
import os
|
|
|
|
import logging
|
|
|
|
import threading
|
|
|
|
import subprocess
|
|
|
|
|
|
|
|
import basicswap.config as cfg
|
|
|
|
import basicswap.contrib.segwit_addr as segwit_addr
|
|
|
|
|
|
|
|
from .rpc import (
|
|
|
|
callrpc,
|
|
|
|
)
|
|
|
|
from .util import (
|
|
|
|
TemporaryError,
|
|
|
|
)
|
|
|
|
from .chainparams import (
|
|
|
|
Coins,
|
|
|
|
chainparams,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class BaseApp:
|
|
|
|
def __init__(self, fp, data_dir, settings, chain, log_name='BasicSwap'):
|
|
|
|
self.log_name = log_name
|
|
|
|
self.fp = fp
|
|
|
|
self.is_running = True
|
|
|
|
self.fail_code = 0
|
|
|
|
|
|
|
|
self.data_dir = data_dir
|
|
|
|
self.chain = chain
|
|
|
|
self.settings = settings
|
|
|
|
self.coin_clients = {}
|
|
|
|
self.coin_interfaces = {}
|
|
|
|
self.mxDB = threading.RLock()
|
|
|
|
self.debug = self.settings.get('debug', False)
|
|
|
|
self.delay_event = threading.Event()
|
|
|
|
self._network = None
|
|
|
|
self.prepareLogging()
|
|
|
|
self.log.info('Network: {}'.format(self.chain))
|
|
|
|
|
|
|
|
def stopRunning(self, with_code=0):
|
|
|
|
self.fail_code = with_code
|
|
|
|
with self.mxDB:
|
|
|
|
self.is_running = False
|
|
|
|
self.delay_event.set()
|
|
|
|
|
|
|
|
def prepareLogging(self):
|
|
|
|
self.log = logging.getLogger(self.log_name)
|
|
|
|
self.log.propagate = False
|
|
|
|
|
|
|
|
# Remove any existing handlers
|
|
|
|
self.log.handlers = []
|
|
|
|
|
|
|
|
formatter = logging.Formatter('%(asctime)s %(levelname)s : %(message)s')
|
|
|
|
stream_stdout = logging.StreamHandler()
|
|
|
|
if self.log_name != 'BasicSwap':
|
|
|
|
stream_stdout.setFormatter(logging.Formatter('%(asctime)s %(name)s %(levelname)s : %(message)s'))
|
|
|
|
else:
|
|
|
|
stream_stdout.setFormatter(formatter)
|
|
|
|
stream_fp = logging.StreamHandler(self.fp)
|
|
|
|
stream_fp.setFormatter(formatter)
|
|
|
|
|
|
|
|
self.log.setLevel(logging.DEBUG if self.debug else logging.INFO)
|
|
|
|
self.log.addHandler(stream_fp)
|
|
|
|
self.log.addHandler(stream_stdout)
|
|
|
|
|
|
|
|
def getChainClientSettings(self, coin):
|
|
|
|
try:
|
|
|
|
return self.settings['chainclients'][chainparams[coin]['name']]
|
|
|
|
except Exception:
|
|
|
|
return {}
|
|
|
|
|
|
|
|
def setDaemonPID(self, name, pid):
|
|
|
|
if isinstance(name, Coins):
|
|
|
|
self.coin_clients[name]['pid'] = pid
|
|
|
|
return
|
|
|
|
for c, v in self.coin_clients.items():
|
|
|
|
if v['name'] == name:
|
|
|
|
v['pid'] = pid
|
|
|
|
|
|
|
|
def getChainDatadirPath(self, coin):
|
|
|
|
datadir = self.coin_clients[coin]['datadir']
|
|
|
|
testnet_name = '' if self.chain == 'mainnet' else chainparams[coin][self.chain].get('name', self.chain)
|
|
|
|
return os.path.join(datadir, testnet_name)
|
|
|
|
|
|
|
|
def getCoinIdFromName(self, coin_name):
|
|
|
|
for c, params in chainparams.items():
|
|
|
|
if coin_name.lower() == params['name'].lower():
|
|
|
|
return c
|
|
|
|
raise ValueError('Unknown coin: {}'.format(coin_name))
|
|
|
|
|
|
|
|
def encodeSegwit(self, coin_type, raw):
|
|
|
|
return segwit_addr.encode(chainparams[coin_type][self.chain]['hrp'], 0, raw)
|
|
|
|
|
|
|
|
def decodeSegwit(self, coin_type, addr):
|
|
|
|
return bytes(segwit_addr.decode(chainparams[coin_type][self.chain]['hrp'], addr)[1])
|
|
|
|
|
|
|
|
def callrpc(self, method, params=[], wallet=None):
|
|
|
|
return callrpc(self.coin_clients[Coins.PART]['rpcport'], self.coin_clients[Coins.PART]['rpcauth'], method, params, wallet)
|
|
|
|
|
|
|
|
def callcoinrpc(self, coin, method, params=[], wallet=None):
|
|
|
|
return callrpc(self.coin_clients[coin]['rpcport'], self.coin_clients[coin]['rpcauth'], method, params, wallet)
|
|
|
|
|
|
|
|
def calltx(self, cmd):
|
|
|
|
bindir = self.coin_clients[Coins.PART]['bindir']
|
|
|
|
command_tx = os.path.join(bindir, cfg.PARTICL_TX)
|
|
|
|
chainname = '' if self.chain == 'mainnet' else (' -' + self.chain)
|
|
|
|
args = command_tx + chainname + ' ' + cmd
|
|
|
|
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
|
|
|
out = p.communicate()
|
|
|
|
if len(out[1]) > 0:
|
|
|
|
raise ValueError('TX error ' + str(out[1]))
|
|
|
|
return out[0].decode('utf-8').strip()
|
|
|
|
|
|
|
|
def callcoincli(self, coin_type, params, wallet=None, timeout=None):
|
|
|
|
bindir = self.coin_clients[coin_type]['bindir']
|
|
|
|
datadir = self.coin_clients[coin_type]['datadir']
|
|
|
|
command_cli = os.path.join(bindir, chainparams[coin_type]['name'] + '-cli' + ('.exe' if os.name == 'nt' else ''))
|
|
|
|
chainname = '' if self.chain == 'mainnet' else (' -' + self.chain)
|
|
|
|
args = command_cli + chainname + ' ' + '-datadir=' + datadir + ' ' + params
|
|
|
|
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
|
|
|
out = p.communicate(timeout=timeout)
|
|
|
|
if len(out[1]) > 0:
|
|
|
|
raise ValueError('CLI error ' + str(out[1]))
|
|
|
|
return out[0].decode('utf-8').strip()
|
|
|
|
|
|
|
|
def is_transient_error(self, ex):
|
|
|
|
if isinstance(ex, TemporaryError):
|
|
|
|
return True
|
|
|
|
str_error = str(ex).lower()
|
|
|
|
return 'read timed out' in str_error or 'no connection to daemon' in str_error
|