mirror of
https://github.com/basicswap/basicswap.git
synced 2024-11-16 15:58:17 +00:00
Reformat with black.
This commit is contained in:
parent
6be9a14335
commit
732c87b013
66 changed files with 16755 additions and 9343 deletions
|
@ -6,7 +6,7 @@ lint_task:
|
|||
- pip install flake8 codespell
|
||||
script:
|
||||
- flake8 --version
|
||||
- PYTHONWARNINGS="ignore" flake8 --ignore=E203,E501,F841,W503,E702,E131 --exclude=basicswap/contrib,basicswap/interface/contrib,messages_pb2.py,.eggs,.tox,bin/install_certifi.py
|
||||
- flake8 --ignore=E203,E501,W503 --exclude=basicswap/contrib,basicswap/interface/contrib,.eggs,.tox,bin/install_certifi.py
|
||||
- codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=tests/lint/spelling.ignore-words.txt -S .git,.eggs,.tox,pgp,*.pyc,*basicswap/contrib,*basicswap/interface/contrib,*mnemonics.py,bin/install_certifi.py,*basicswap/static
|
||||
|
||||
test_task:
|
||||
|
|
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
|
@ -20,7 +20,7 @@ jobs:
|
|||
pip install flake8 codespell
|
||||
- name: Running flake8
|
||||
run: |
|
||||
flake8 --ignore=E203,E501,F841,W503 --per-file-ignores="basicswap/interface/bch.py:E131,E702" --exclude=basicswap/contrib,basicswap/interface/contrib,messages_pb2.py,.eggs,.tox,bin/install_certifi.py
|
||||
flake8 --ignore=E203,E501,W503 --exclude=basicswap/contrib,basicswap/interface/contrib,.eggs,.tox,bin/install_certifi.py
|
||||
- name: Running codespell
|
||||
run: |
|
||||
codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=tests/lint/spelling.ignore-words.txt -S .git,.eggs,.tox,pgp,*.pyc,*basicswap/contrib,*basicswap/interface/contrib,*mnemonics.py,bin/install_certifi.py,*basicswap/static
|
||||
|
|
|
@ -35,7 +35,7 @@ def getaddrinfo_tor(*args):
|
|||
|
||||
|
||||
class BaseApp:
|
||||
def __init__(self, fp, data_dir, settings, chain, log_name='BasicSwap'):
|
||||
def __init__(self, fp, data_dir, settings, chain, log_name="BasicSwap"):
|
||||
self.log_name = log_name
|
||||
self.fp = fp
|
||||
self.fail_code = 0
|
||||
|
@ -47,19 +47,19 @@ class BaseApp:
|
|||
self.coin_clients = {}
|
||||
self.coin_interfaces = {}
|
||||
self.mxDB = threading.Lock()
|
||||
self.debug = self.settings.get('debug', False)
|
||||
self.debug = self.settings.get("debug", False)
|
||||
self.delay_event = threading.Event()
|
||||
self.chainstate_delay_event = threading.Event()
|
||||
|
||||
self._network = None
|
||||
self.prepareLogging()
|
||||
self.log.info('Network: {}'.format(self.chain))
|
||||
self.log.info("Network: {}".format(self.chain))
|
||||
|
||||
self.use_tor_proxy = self.settings.get('use_tor', False)
|
||||
self.tor_proxy_host = self.settings.get('tor_proxy_host', '127.0.0.1')
|
||||
self.tor_proxy_port = self.settings.get('tor_proxy_port', 9050)
|
||||
self.tor_control_password = self.settings.get('tor_control_password', None)
|
||||
self.tor_control_port = self.settings.get('tor_control_port', 9051)
|
||||
self.use_tor_proxy = self.settings.get("use_tor", False)
|
||||
self.tor_proxy_host = self.settings.get("tor_proxy_host", "127.0.0.1")
|
||||
self.tor_proxy_port = self.settings.get("tor_proxy_port", 9050)
|
||||
self.tor_control_password = self.settings.get("tor_control_password", None)
|
||||
self.tor_control_port = self.settings.get("tor_control_port", 9051)
|
||||
self.default_socket = socket.socket
|
||||
self.default_socket_timeout = socket.getdefaulttimeout()
|
||||
self.default_socket_getaddrinfo = socket.getaddrinfo
|
||||
|
@ -77,10 +77,17 @@ class BaseApp:
|
|||
# Remove any existing handlers
|
||||
self.log.handlers = []
|
||||
|
||||
formatter = logging.Formatter('%(asctime)s %(levelname)s : %(message)s', '%Y-%m-%d %H:%M:%S')
|
||||
formatter = logging.Formatter(
|
||||
"%(asctime)s %(levelname)s : %(message)s", "%Y-%m-%d %H:%M:%S"
|
||||
)
|
||||
stream_stdout = logging.StreamHandler()
|
||||
if self.log_name != 'BasicSwap':
|
||||
stream_stdout.setFormatter(logging.Formatter('%(asctime)s %(name)s %(levelname)s : %(message)s', '%Y-%m-%d %H:%M:%S'))
|
||||
if self.log_name != "BasicSwap":
|
||||
stream_stdout.setFormatter(
|
||||
logging.Formatter(
|
||||
"%(asctime)s %(name)s %(levelname)s : %(message)s",
|
||||
"%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
)
|
||||
else:
|
||||
stream_stdout.setFormatter(formatter)
|
||||
stream_fp = logging.StreamHandler(self.fp)
|
||||
|
@ -92,68 +99,91 @@ class BaseApp:
|
|||
|
||||
def getChainClientSettings(self, coin):
|
||||
try:
|
||||
return self.settings['chainclients'][chainparams[coin]['name']]
|
||||
return self.settings["chainclients"][chainparams[coin]["name"]]
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
def setDaemonPID(self, name, pid) -> None:
|
||||
if isinstance(name, Coins):
|
||||
self.coin_clients[name]['pid'] = pid
|
||||
self.coin_clients[name]["pid"] = pid
|
||||
return
|
||||
for c, v in self.coin_clients.items():
|
||||
if v['name'] == name:
|
||||
v['pid'] = pid
|
||||
if v["name"] == name:
|
||||
v["pid"] = pid
|
||||
|
||||
def getChainDatadirPath(self, coin) -> str:
|
||||
datadir = self.coin_clients[coin]['datadir']
|
||||
testnet_name = '' if self.chain == 'mainnet' else chainparams[coin][self.chain].get('name', self.chain)
|
||||
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: str):
|
||||
for c, params in chainparams.items():
|
||||
if coin_name.lower() == params['name'].lower():
|
||||
if coin_name.lower() == params["name"].lower():
|
||||
return c
|
||||
raise ValueError('Unknown coin: {}'.format(coin_name))
|
||||
raise ValueError("Unknown coin: {}".format(coin_name))
|
||||
|
||||
def callrpc(self, method, params=[], wallet=None):
|
||||
cc = self.coin_clients[Coins.PART]
|
||||
return callrpc(cc['rpcport'], cc['rpcauth'], method, params, wallet, cc['rpchost'])
|
||||
return callrpc(
|
||||
cc["rpcport"], cc["rpcauth"], method, params, wallet, cc["rpchost"]
|
||||
)
|
||||
|
||||
def callcoinrpc(self, coin, method, params=[], wallet=None):
|
||||
cc = self.coin_clients[coin]
|
||||
return callrpc(cc['rpcport'], cc['rpcauth'], method, params, wallet, cc['rpchost'])
|
||||
return callrpc(
|
||||
cc["rpcport"], cc["rpcauth"], method, params, wallet, cc["rpchost"]
|
||||
)
|
||||
|
||||
def callcoincli(self, coin_type, params, wallet=None, timeout=None):
|
||||
bindir = self.coin_clients[coin_type]['bindir']
|
||||
datadir = self.coin_clients[coin_type]['datadir']
|
||||
cli_bin: str = chainparams[coin_type].get('cli_binname', chainparams[coin_type]['name'] + '-cli')
|
||||
command_cli = os.path.join(bindir, cli_bin + ('.exe' if os.name == 'nt' else ''))
|
||||
args = [command_cli, ]
|
||||
if self.chain != 'mainnet':
|
||||
args.append('-' + self.chain)
|
||||
args.append('-datadir=' + datadir)
|
||||
bindir = self.coin_clients[coin_type]["bindir"]
|
||||
datadir = self.coin_clients[coin_type]["datadir"]
|
||||
cli_bin: str = chainparams[coin_type].get(
|
||||
"cli_binname", chainparams[coin_type]["name"] + "-cli"
|
||||
)
|
||||
command_cli = os.path.join(
|
||||
bindir, cli_bin + (".exe" if os.name == "nt" else "")
|
||||
)
|
||||
args = [
|
||||
command_cli,
|
||||
]
|
||||
if self.chain != "mainnet":
|
||||
args.append("-" + self.chain)
|
||||
args.append("-datadir=" + datadir)
|
||||
args += shlex.split(params)
|
||||
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
p = subprocess.Popen(
|
||||
args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
out = p.communicate(timeout=timeout)
|
||||
if len(out[1]) > 0:
|
||||
raise ValueError('CLI error ' + str(out[1]))
|
||||
return out[0].decode('utf-8').strip()
|
||||
raise ValueError("CLI error " + str(out[1]))
|
||||
return out[0].decode("utf-8").strip()
|
||||
|
||||
def is_transient_error(self, ex) -> bool:
|
||||
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
|
||||
return "read timed out" in str_error or "no connection to daemon" in str_error
|
||||
|
||||
def setConnectionParameters(self, timeout=120):
|
||||
opener = urllib.request.build_opener()
|
||||
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
|
||||
opener.addheaders = [("User-agent", "Mozilla/5.0")]
|
||||
urllib.request.install_opener(opener)
|
||||
|
||||
if self.use_tor_proxy:
|
||||
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, self.tor_proxy_host, self.tor_proxy_port, rdns=True)
|
||||
socks.setdefaultproxy(
|
||||
socks.PROXY_TYPE_SOCKS5,
|
||||
self.tor_proxy_host,
|
||||
self.tor_proxy_port,
|
||||
rdns=True,
|
||||
)
|
||||
socket.socket = socks.socksocket
|
||||
socket.getaddrinfo = getaddrinfo_tor # Without this accessing .onion links would fail
|
||||
socket.getaddrinfo = (
|
||||
getaddrinfo_tor # Without this accessing .onion links would fail
|
||||
)
|
||||
|
||||
socket.setdefaulttimeout(timeout)
|
||||
|
||||
|
@ -166,10 +196,16 @@ class BaseApp:
|
|||
def readURL(self, url: str, timeout: int = 120, headers={}) -> bytes:
|
||||
open_handler = None
|
||||
if self.use_tor_proxy:
|
||||
open_handler = SocksiPyHandler(socks.PROXY_TYPE_SOCKS5, self.tor_proxy_host, self.tor_proxy_port)
|
||||
opener = urllib.request.build_opener(open_handler) if self.use_tor_proxy else urllib.request.build_opener()
|
||||
open_handler = SocksiPyHandler(
|
||||
socks.PROXY_TYPE_SOCKS5, self.tor_proxy_host, self.tor_proxy_port
|
||||
)
|
||||
opener = (
|
||||
urllib.request.build_opener(open_handler)
|
||||
if self.use_tor_proxy
|
||||
else urllib.request.build_opener()
|
||||
)
|
||||
if headers is None:
|
||||
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
|
||||
opener.addheaders = [("User-agent", "Mozilla/5.0")]
|
||||
request = urllib.request.Request(url, headers=headers)
|
||||
return opener.open(request, timeout=timeout).read()
|
||||
|
||||
|
@ -180,7 +216,9 @@ class BaseApp:
|
|||
|
||||
def torControl(self, query):
|
||||
try:
|
||||
command = 'AUTHENTICATE "{}"\r\n{}\r\nQUIT\r\n'.format(self.tor_control_password, query).encode('utf-8')
|
||||
command = 'AUTHENTICATE "{}"\r\n{}\r\nQUIT\r\n'.format(
|
||||
self.tor_control_password, query
|
||||
).encode("utf-8")
|
||||
c = socket.create_connection((self.tor_proxy_host, self.tor_control_port))
|
||||
c.send(command)
|
||||
response = bytearray()
|
||||
|
@ -192,23 +230,23 @@ class BaseApp:
|
|||
c.close()
|
||||
return response
|
||||
except Exception as e:
|
||||
self.log.error(f'torControl {e}')
|
||||
self.log.error(f"torControl {e}")
|
||||
return
|
||||
|
||||
def getTime(self) -> int:
|
||||
return int(time.time()) + self.mock_time_offset
|
||||
|
||||
def setMockTimeOffset(self, new_offset: int) -> None:
|
||||
self.log.warning(f'Setting mocktime to {new_offset}')
|
||||
self.log.warning(f"Setting mocktime to {new_offset}")
|
||||
self.mock_time_offset = new_offset
|
||||
|
||||
def get_int_setting(self, name: str, default_v: int, min_v: int, max_v) -> int:
|
||||
value: int = self.settings.get(name, default_v)
|
||||
if value < min_v:
|
||||
self.log.warning(f'Setting {name} to {min_v}')
|
||||
self.log.warning(f"Setting {name} to {min_v}")
|
||||
value = min_v
|
||||
if value > max_v:
|
||||
self.log.warning(f'Setting {name} to {max_v}')
|
||||
self.log.warning(f"Setting {name} to {max_v}")
|
||||
value = max_v
|
||||
return value
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -76,13 +76,13 @@ class OfferStates(IntEnum):
|
|||
|
||||
class BidStates(IntEnum):
|
||||
BID_SENT = 1
|
||||
BID_RECEIVING = 2 # Partially received
|
||||
BID_RECEIVING = 2 # Partially received
|
||||
BID_RECEIVED = 3
|
||||
BID_RECEIVING_ACC = 4 # Partially received accept message
|
||||
BID_ACCEPTED = 5 # BidAcceptMessage received/sent
|
||||
SWAP_INITIATED = 6 # Initiate txn validated
|
||||
SWAP_PARTICIPATING = 7 # Participate txn validated
|
||||
SWAP_COMPLETED = 8 # All swap txns spent
|
||||
BID_RECEIVING_ACC = 4 # Partially received accept message
|
||||
BID_ACCEPTED = 5 # BidAcceptMessage received/sent
|
||||
SWAP_INITIATED = 6 # Initiate txn validated
|
||||
SWAP_PARTICIPATING = 7 # Participate txn validated
|
||||
SWAP_COMPLETED = 8 # All swap txns spent
|
||||
XMR_SWAP_SCRIPT_COIN_LOCKED = 9
|
||||
XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX = 10
|
||||
XMR_SWAP_NOSCRIPT_COIN_LOCKED = 11
|
||||
|
@ -96,13 +96,13 @@ class BidStates(IntEnum):
|
|||
XMR_SWAP_FAILED = 19
|
||||
SWAP_DELAYING = 20
|
||||
SWAP_TIMEDOUT = 21
|
||||
BID_ABANDONED = 22 # Bid will no longer be processed
|
||||
BID_ERROR = 23 # An error occurred
|
||||
BID_ABANDONED = 22 # Bid will no longer be processed
|
||||
BID_ERROR = 23 # An error occurred
|
||||
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
|
||||
XMR_SWAP_MSG_SCRIPT_LOCK_TX_SIGS = 27 # XmrBidLockTxSigsMessage
|
||||
XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX = 28 # XmrBidLockSpendTxMessage
|
||||
BID_REQUEST_SENT = 29
|
||||
BID_REQUEST_ACCEPTED = 30
|
||||
BID_EXPIRED = 31
|
||||
|
@ -231,12 +231,12 @@ class AutomationOverrideOptions(IntEnum):
|
|||
|
||||
def strAutomationOverrideOption(option):
|
||||
if option == AutomationOverrideOptions.DEFAULT:
|
||||
return 'Default'
|
||||
return "Default"
|
||||
if option == AutomationOverrideOptions.ALWAYS_ACCEPT:
|
||||
return 'Always Accept'
|
||||
return "Always Accept"
|
||||
if option == AutomationOverrideOptions.NEVER_ACCEPT:
|
||||
return 'Never Accept'
|
||||
return 'Unknown'
|
||||
return "Never Accept"
|
||||
return "Unknown"
|
||||
|
||||
|
||||
class VisibilityOverrideOptions(IntEnum):
|
||||
|
@ -247,250 +247,253 @@ class VisibilityOverrideOptions(IntEnum):
|
|||
|
||||
def strVisibilityOverrideOption(option):
|
||||
if option == VisibilityOverrideOptions.DEFAULT:
|
||||
return 'Default'
|
||||
return "Default"
|
||||
if option == VisibilityOverrideOptions.HIDE:
|
||||
return 'Hide'
|
||||
return "Hide"
|
||||
if option == VisibilityOverrideOptions.BLOCK:
|
||||
return 'Block'
|
||||
return 'Unknown'
|
||||
return "Block"
|
||||
return "Unknown"
|
||||
|
||||
|
||||
def strOfferState(state):
|
||||
if state == OfferStates.OFFER_SENT:
|
||||
return 'Sent'
|
||||
return "Sent"
|
||||
if state == OfferStates.OFFER_RECEIVED:
|
||||
return 'Received'
|
||||
return "Received"
|
||||
if state == OfferStates.OFFER_ABANDONED:
|
||||
return 'Abandoned'
|
||||
return "Abandoned"
|
||||
if state == OfferStates.OFFER_EXPIRED:
|
||||
return 'Expired'
|
||||
return 'Unknown'
|
||||
return "Expired"
|
||||
return "Unknown"
|
||||
|
||||
|
||||
def strBidState(state):
|
||||
if state == BidStates.BID_SENT:
|
||||
return 'Sent'
|
||||
return "Sent"
|
||||
if state == BidStates.BID_RECEIVING:
|
||||
return 'Receiving'
|
||||
return "Receiving"
|
||||
if state == BidStates.BID_RECEIVING_ACC:
|
||||
return 'Receiving accept'
|
||||
return "Receiving accept"
|
||||
if state == BidStates.BID_RECEIVED:
|
||||
return 'Received'
|
||||
return "Received"
|
||||
if state == BidStates.BID_ACCEPTED:
|
||||
return 'Accepted'
|
||||
return "Accepted"
|
||||
if state == BidStates.SWAP_INITIATED:
|
||||
return 'Initiated'
|
||||
return "Initiated"
|
||||
if state == BidStates.SWAP_PARTICIPATING:
|
||||
return 'Participating'
|
||||
return "Participating"
|
||||
if state == BidStates.SWAP_COMPLETED:
|
||||
return 'Completed'
|
||||
return "Completed"
|
||||
if state == BidStates.SWAP_TIMEDOUT:
|
||||
return 'Timed-out'
|
||||
return "Timed-out"
|
||||
if state == BidStates.BID_ABANDONED:
|
||||
return 'Abandoned'
|
||||
return "Abandoned"
|
||||
if state == BidStates.BID_STALLED_FOR_TEST:
|
||||
return 'Stalled (debug)'
|
||||
return "Stalled (debug)"
|
||||
if state == BidStates.BID_ERROR:
|
||||
return 'Error'
|
||||
return "Error"
|
||||
if state == BidStates.BID_REJECTED:
|
||||
return 'Rejected'
|
||||
return "Rejected"
|
||||
if state == BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED:
|
||||
return 'Script coin locked'
|
||||
return "Script coin locked"
|
||||
if state == BidStates.XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX:
|
||||
return 'Script coin spend tx valid'
|
||||
return "Script coin spend tx valid"
|
||||
if state == BidStates.XMR_SWAP_NOSCRIPT_COIN_LOCKED:
|
||||
return 'Scriptless coin locked'
|
||||
return "Scriptless coin locked"
|
||||
if state == BidStates.XMR_SWAP_LOCK_RELEASED:
|
||||
return 'Script coin lock released'
|
||||
return "Script coin lock released"
|
||||
if state == BidStates.XMR_SWAP_SCRIPT_TX_REDEEMED:
|
||||
return 'Script tx redeemed'
|
||||
return "Script tx redeemed"
|
||||
if state == BidStates.XMR_SWAP_SCRIPT_TX_PREREFUND:
|
||||
return 'Script pre-refund tx in chain'
|
||||
return "Script pre-refund tx in chain"
|
||||
if state == BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED:
|
||||
return 'Scriptless tx redeemed'
|
||||
return "Scriptless tx redeemed"
|
||||
if state == BidStates.XMR_SWAP_NOSCRIPT_TX_RECOVERED:
|
||||
return 'Scriptless tx recovered'
|
||||
return "Scriptless tx recovered"
|
||||
if state == BidStates.XMR_SWAP_FAILED_REFUNDED:
|
||||
return 'Failed, refunded'
|
||||
return "Failed, refunded"
|
||||
if state == BidStates.XMR_SWAP_FAILED_SWIPED:
|
||||
return 'Failed, swiped'
|
||||
return "Failed, swiped"
|
||||
if state == BidStates.XMR_SWAP_FAILED:
|
||||
return 'Failed'
|
||||
return "Failed"
|
||||
if state == BidStates.SWAP_DELAYING:
|
||||
return 'Delaying'
|
||||
return "Delaying"
|
||||
if state == BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_TX_SIGS:
|
||||
return 'Exchanged script lock tx sigs msg'
|
||||
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 "Exchanged script lock spend tx msg"
|
||||
if state == BidStates.BID_REQUEST_SENT:
|
||||
return 'Request sent'
|
||||
return "Request sent"
|
||||
if state == BidStates.BID_REQUEST_ACCEPTED:
|
||||
return 'Request accepted'
|
||||
return "Request accepted"
|
||||
if state == BidStates.BID_STATE_UNKNOWN:
|
||||
return 'Unknown bid state'
|
||||
return "Unknown bid state"
|
||||
if state == BidStates.BID_EXPIRED:
|
||||
return 'Expired'
|
||||
return 'Unknown' + ' ' + str(state)
|
||||
return "Expired"
|
||||
return "Unknown" + " " + str(state)
|
||||
|
||||
|
||||
def strTxState(state):
|
||||
if state == TxStates.TX_NONE:
|
||||
return 'None'
|
||||
return "None"
|
||||
if state == TxStates.TX_SENT:
|
||||
return 'Sent'
|
||||
return "Sent"
|
||||
if state == TxStates.TX_CONFIRMED:
|
||||
return 'Confirmed'
|
||||
return "Confirmed"
|
||||
if state == TxStates.TX_REDEEMED:
|
||||
return 'Redeemed'
|
||||
return "Redeemed"
|
||||
if state == TxStates.TX_REFUNDED:
|
||||
return 'Refunded'
|
||||
return "Refunded"
|
||||
if state == TxStates.TX_IN_MEMPOOL:
|
||||
return 'In Mempool'
|
||||
return "In Mempool"
|
||||
if state == TxStates.TX_IN_CHAIN:
|
||||
return 'In Chain'
|
||||
return 'Unknown'
|
||||
return "In Chain"
|
||||
return "Unknown"
|
||||
|
||||
|
||||
def strTxType(tx_type):
|
||||
if tx_type == TxTypes.XMR_SWAP_A_LOCK:
|
||||
return 'Chain A Lock Tx'
|
||||
return "Chain A Lock Tx"
|
||||
if tx_type == TxTypes.XMR_SWAP_A_LOCK_SPEND:
|
||||
return 'Chain A Lock Spend Tx'
|
||||
return "Chain A Lock Spend Tx"
|
||||
if tx_type == TxTypes.XMR_SWAP_A_LOCK_REFUND:
|
||||
return 'Chain A Lock Refund Tx'
|
||||
return "Chain A Lock Refund Tx"
|
||||
if tx_type == TxTypes.XMR_SWAP_A_LOCK_REFUND_SPEND:
|
||||
return 'Chain A Lock Refund Spend Tx'
|
||||
return "Chain A Lock Refund Spend Tx"
|
||||
if tx_type == TxTypes.XMR_SWAP_A_LOCK_REFUND_SWIPE:
|
||||
return 'Chain A Lock Refund Swipe Tx'
|
||||
return "Chain A Lock Refund Swipe Tx"
|
||||
if tx_type == TxTypes.XMR_SWAP_B_LOCK:
|
||||
return 'Chain B Lock Tx'
|
||||
return "Chain B Lock Tx"
|
||||
if tx_type == TxTypes.ITX_PRE_FUNDED:
|
||||
return 'Funded mock initiate Tx'
|
||||
return "Funded mock initiate Tx"
|
||||
if tx_type == TxTypes.BCH_MERCY:
|
||||
return 'BCH Mercy Tx'
|
||||
return 'Unknown'
|
||||
return "BCH Mercy Tx"
|
||||
return "Unknown"
|
||||
|
||||
|
||||
def strAddressType(addr_type):
|
||||
if addr_type == AddressTypes.OFFER:
|
||||
return 'Offer'
|
||||
return "Offer"
|
||||
if addr_type == AddressTypes.BID:
|
||||
return 'Bid'
|
||||
return "Bid"
|
||||
if addr_type == AddressTypes.RECV_OFFER:
|
||||
return 'Offer recv'
|
||||
return "Offer recv"
|
||||
if addr_type == AddressTypes.SEND_OFFER:
|
||||
return 'Offer send'
|
||||
return 'Unknown'
|
||||
return "Offer send"
|
||||
return "Unknown"
|
||||
|
||||
|
||||
def getLockName(lock_type):
|
||||
if lock_type == TxLockTypes.SEQUENCE_LOCK_BLOCKS:
|
||||
return 'Sequence lock, blocks'
|
||||
return "Sequence lock, blocks"
|
||||
if lock_type == TxLockTypes.SEQUENCE_LOCK_TIME:
|
||||
return 'Sequence lock, time'
|
||||
return "Sequence lock, time"
|
||||
if lock_type == TxLockTypes.ABS_LOCK_BLOCKS:
|
||||
return 'blocks'
|
||||
return "blocks"
|
||||
if lock_type == TxLockTypes.ABS_LOCK_TIME:
|
||||
return 'time'
|
||||
return "time"
|
||||
|
||||
|
||||
def describeEventEntry(event_type, event_msg):
|
||||
if event_type == EventLogTypes.FAILED_TX_B_LOCK_PUBLISH:
|
||||
return 'Failed to publish lock tx B'
|
||||
return "Failed to publish lock tx B"
|
||||
if event_type == EventLogTypes.LOCK_TX_A_PUBLISHED:
|
||||
return 'Lock tx A published'
|
||||
return "Lock tx A published"
|
||||
if event_type == EventLogTypes.LOCK_TX_B_PUBLISHED:
|
||||
return 'Lock tx B published'
|
||||
return "Lock tx B published"
|
||||
if event_type == EventLogTypes.FAILED_TX_B_SPEND:
|
||||
return 'Failed to publish lock tx B spend: ' + event_msg
|
||||
return "Failed to publish lock tx B spend: " + event_msg
|
||||
if event_type == EventLogTypes.LOCK_TX_A_SEEN:
|
||||
return 'Lock tx A seen in chain'
|
||||
return "Lock tx A seen in chain"
|
||||
if event_type == EventLogTypes.LOCK_TX_A_CONFIRMED:
|
||||
return 'Lock tx A confirmed in chain'
|
||||
return "Lock tx A confirmed in chain"
|
||||
if event_type == EventLogTypes.LOCK_TX_B_SEEN:
|
||||
return 'Lock tx B seen in chain'
|
||||
return "Lock tx B seen in chain"
|
||||
if event_type == EventLogTypes.LOCK_TX_B_CONFIRMED:
|
||||
return 'Lock tx B confirmed in chain'
|
||||
return "Lock tx B confirmed in chain"
|
||||
if event_type == EventLogTypes.LOCK_TX_B_IN_MEMPOOL:
|
||||
return 'Lock tx B seen in mempool'
|
||||
return "Lock tx B seen in mempool"
|
||||
if event_type == EventLogTypes.DEBUG_TWEAK_APPLIED:
|
||||
return 'Debug tweak applied ' + event_msg
|
||||
return "Debug tweak applied " + event_msg
|
||||
if event_type == EventLogTypes.FAILED_TX_B_REFUND:
|
||||
return 'Failed to publish lock tx B refund'
|
||||
return "Failed to publish lock tx B refund"
|
||||
if event_type == EventLogTypes.LOCK_TX_B_INVALID:
|
||||
return 'Detected invalid lock Tx B'
|
||||
return "Detected invalid lock Tx B"
|
||||
if event_type == EventLogTypes.LOCK_TX_A_REFUND_TX_PUBLISHED:
|
||||
return 'Lock tx A refund tx published'
|
||||
return "Lock tx A refund tx published"
|
||||
if event_type == EventLogTypes.LOCK_TX_A_REFUND_SPEND_TX_PUBLISHED:
|
||||
return 'Lock tx A refund spend tx published'
|
||||
return "Lock tx A refund spend tx published"
|
||||
if event_type == EventLogTypes.LOCK_TX_A_REFUND_SWIPE_TX_PUBLISHED:
|
||||
return 'Lock tx A refund swipe tx published'
|
||||
return "Lock tx A refund swipe tx published"
|
||||
if event_type == EventLogTypes.LOCK_TX_B_REFUND_TX_PUBLISHED:
|
||||
return 'Lock tx B refund tx published'
|
||||
return "Lock tx B refund tx published"
|
||||
if event_type == EventLogTypes.LOCK_TX_A_SPEND_TX_PUBLISHED:
|
||||
return 'Lock tx A spend tx published'
|
||||
return "Lock tx A spend tx published"
|
||||
if event_type == EventLogTypes.LOCK_TX_B_SPEND_TX_PUBLISHED:
|
||||
return 'Lock tx B spend tx published'
|
||||
return "Lock tx B spend tx published"
|
||||
if event_type == EventLogTypes.LOCK_TX_A_REFUND_TX_SEEN:
|
||||
return 'Lock tx A refund tx seen in chain'
|
||||
return "Lock tx A refund tx seen in chain"
|
||||
if event_type == EventLogTypes.LOCK_TX_A_REFUND_SPEND_TX_SEEN:
|
||||
return 'Lock tx A refund spend tx seen in chain'
|
||||
return "Lock tx A refund spend tx seen in chain"
|
||||
if event_type == EventLogTypes.SYSTEM_WARNING:
|
||||
return 'Warning: ' + event_msg
|
||||
return "Warning: " + event_msg
|
||||
if event_type == EventLogTypes.ERROR:
|
||||
return 'Error: ' + event_msg
|
||||
return "Error: " + event_msg
|
||||
if event_type == EventLogTypes.AUTOMATION_CONSTRAINT:
|
||||
return 'Failed auto accepting'
|
||||
return "Failed auto accepting"
|
||||
if event_type == EventLogTypes.AUTOMATION_ACCEPTING_BID:
|
||||
return 'Auto accepting'
|
||||
return "Auto accepting"
|
||||
if event_type == EventLogTypes.ITX_PUBLISHED:
|
||||
return 'Initiate tx published'
|
||||
return "Initiate tx published"
|
||||
if event_type == EventLogTypes.ITX_REDEEM_PUBLISHED:
|
||||
return 'Initiate tx redeem tx published'
|
||||
return "Initiate tx redeem tx published"
|
||||
if event_type == EventLogTypes.ITX_REFUND_PUBLISHED:
|
||||
return 'Initiate tx refund tx published'
|
||||
return "Initiate tx refund tx published"
|
||||
if event_type == EventLogTypes.PTX_PUBLISHED:
|
||||
return 'Participate tx published'
|
||||
return "Participate tx published"
|
||||
if event_type == EventLogTypes.PTX_REDEEM_PUBLISHED:
|
||||
return 'Participate tx redeem tx published'
|
||||
return "Participate tx redeem tx published"
|
||||
if event_type == EventLogTypes.PTX_REFUND_PUBLISHED:
|
||||
return 'Participate tx refund tx published'
|
||||
return "Participate tx refund tx published"
|
||||
if event_type == EventLogTypes.BCH_MERCY_TX_FOUND:
|
||||
return 'BCH mercy tx found'
|
||||
return "BCH mercy tx found"
|
||||
if event_type == EventLogTypes.BCH_MERCY_TX_PUBLISHED:
|
||||
return 'Lock tx B mercy tx published'
|
||||
return "Lock tx B mercy tx published"
|
||||
|
||||
|
||||
def getVoutByAddress(txjs, p2sh):
|
||||
for o in txjs['vout']:
|
||||
for o in txjs["vout"]:
|
||||
try:
|
||||
if 'address' in o['scriptPubKey'] and o['scriptPubKey']['address'] == p2sh:
|
||||
return o['n']
|
||||
if p2sh in o['scriptPubKey']['addresses']:
|
||||
return o['n']
|
||||
if "address" in o["scriptPubKey"] and o["scriptPubKey"]["address"] == p2sh:
|
||||
return o["n"]
|
||||
if p2sh in o["scriptPubKey"]["addresses"]:
|
||||
return o["n"]
|
||||
except Exception:
|
||||
pass
|
||||
raise ValueError('Address output not found in txn')
|
||||
raise ValueError("Address output not found in txn")
|
||||
|
||||
|
||||
def getVoutByScriptPubKey(txjs, scriptPubKey_hex: str) -> int:
|
||||
for o in txjs['vout']:
|
||||
for o in txjs["vout"]:
|
||||
try:
|
||||
if scriptPubKey_hex == o['scriptPubKey']['hex']:
|
||||
return o['n']
|
||||
if scriptPubKey_hex == o["scriptPubKey"]["hex"]:
|
||||
return o["n"]
|
||||
except Exception:
|
||||
pass
|
||||
raise ValueError('scriptPubKey output not found in txn')
|
||||
raise ValueError("scriptPubKey output not found in txn")
|
||||
|
||||
|
||||
def replaceAddrPrefix(addr, coin_type, chain_name, addr_type='pubkey_address'):
|
||||
return encodeAddress(bytes((chainparams[coin_type][chain_name][addr_type],)) + decodeAddress(addr)[1:])
|
||||
def replaceAddrPrefix(addr, coin_type, chain_name, addr_type="pubkey_address"):
|
||||
return encodeAddress(
|
||||
bytes((chainparams[coin_type][chain_name][addr_type],))
|
||||
+ decodeAddress(addr)[1:]
|
||||
)
|
||||
|
||||
|
||||
def getOfferProofOfFundsHash(offer_msg, offer_addr):
|
||||
# TODO: Hash must not include proof_of_funds sig if it exists in offer_msg
|
||||
h = hashlib.sha256()
|
||||
h.update(offer_addr.encode('utf-8'))
|
||||
h.update(offer_addr.encode("utf-8"))
|
||||
offer_bytes = offer_msg.to_bytes()
|
||||
h.update(offer_bytes)
|
||||
return h.digest()
|
||||
|
@ -500,33 +503,40 @@ def getLastBidState(packed_states):
|
|||
num_states = len(packed_states) // 12
|
||||
if num_states < 2:
|
||||
return BidStates.BID_STATE_UNKNOWN
|
||||
return struct.unpack_from('<i', packed_states[(num_states - 2) * 12:])[0]
|
||||
return struct.unpack_from("<i", packed_states[(num_states - 2) * 12 :])[0]
|
||||
try:
|
||||
num_states = len(packed_states) // 12
|
||||
if num_states < 2:
|
||||
return BidStates.BID_STATE_UNKNOWN
|
||||
return struct.unpack_from('<i', packed_states[(num_states - 2) * 12:])[0]
|
||||
return struct.unpack_from("<i", packed_states[(num_states - 2) * 12 :])[0]
|
||||
except Exception:
|
||||
return BidStates.BID_STATE_UNKNOWN
|
||||
|
||||
|
||||
def strSwapType(swap_type):
|
||||
if swap_type == SwapTypes.SELLER_FIRST:
|
||||
return 'seller_first'
|
||||
return "seller_first"
|
||||
if swap_type == SwapTypes.XMR_SWAP:
|
||||
return 'xmr_swap'
|
||||
return "xmr_swap"
|
||||
return None
|
||||
|
||||
|
||||
def strSwapDesc(swap_type):
|
||||
if swap_type == SwapTypes.SELLER_FIRST:
|
||||
return 'Secret Hash'
|
||||
return "Secret Hash"
|
||||
if swap_type == SwapTypes.XMR_SWAP:
|
||||
return 'Adaptor Sig'
|
||||
return "Adaptor Sig"
|
||||
return None
|
||||
|
||||
|
||||
inactive_states = [BidStates.SWAP_COMPLETED, BidStates.BID_ERROR, BidStates.BID_REJECTED, BidStates.SWAP_TIMEDOUT, BidStates.BID_ABANDONED, BidStates.BID_EXPIRED]
|
||||
inactive_states = [
|
||||
BidStates.SWAP_COMPLETED,
|
||||
BidStates.BID_ERROR,
|
||||
BidStates.BID_REJECTED,
|
||||
BidStates.SWAP_TIMEDOUT,
|
||||
BidStates.BID_ABANDONED,
|
||||
BidStates.BID_EXPIRED,
|
||||
]
|
||||
|
||||
|
||||
def isActiveBidState(state):
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,6 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2019-2024 tecnovert
|
||||
# Copyright (c) 2024 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -32,7 +33,7 @@ swap_client = None
|
|||
|
||||
|
||||
class Daemon:
|
||||
__slots__ = ('handle', 'files')
|
||||
__slots__ = ("handle", "files")
|
||||
|
||||
def __init__(self, handle, files):
|
||||
self.handle = handle
|
||||
|
@ -41,14 +42,14 @@ class Daemon:
|
|||
|
||||
def is_known_coin(coin_name: str) -> bool:
|
||||
for k, v in chainparams.items():
|
||||
if coin_name == v['name']:
|
||||
if coin_name == v["name"]:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
global swap_client
|
||||
logger.info('Signal %d detected, ending program.' % (sig))
|
||||
logger.info("Signal %d detected, ending program." % (sig))
|
||||
if swap_client is not None:
|
||||
swap_client.stopRunning()
|
||||
|
||||
|
@ -59,9 +60,9 @@ def startDaemon(node_dir, bin_dir, daemon_bin, opts=[], extra_config={}):
|
|||
|
||||
# Rewrite litecoin.conf for 0.21.3
|
||||
# TODO: Remove
|
||||
ltc_conf_path = os.path.join(datadir_path, 'litecoin.conf')
|
||||
ltc_conf_path = os.path.join(datadir_path, "litecoin.conf")
|
||||
if os.path.exists(ltc_conf_path):
|
||||
config_to_add = ['blockfilterindex=0', 'peerblockfilters=0']
|
||||
config_to_add = ["blockfilterindex=0", "peerblockfilters=0"]
|
||||
with open(ltc_conf_path) as fp:
|
||||
for line in fp:
|
||||
line = line.strip()
|
||||
|
@ -69,23 +70,30 @@ def startDaemon(node_dir, bin_dir, daemon_bin, opts=[], extra_config={}):
|
|||
config_to_add.remove(line)
|
||||
|
||||
if len(config_to_add) > 0:
|
||||
logger.info('Rewriting litecoin.conf')
|
||||
shutil.copyfile(ltc_conf_path, ltc_conf_path + '.last')
|
||||
with open(ltc_conf_path, 'a') as fp:
|
||||
logger.info("Rewriting litecoin.conf")
|
||||
shutil.copyfile(ltc_conf_path, ltc_conf_path + ".last")
|
||||
with open(ltc_conf_path, "a") as fp:
|
||||
for line in config_to_add:
|
||||
fp.write(line + '\n')
|
||||
fp.write(line + "\n")
|
||||
|
||||
args = [daemon_bin, ]
|
||||
add_datadir: bool = extra_config.get('add_datadir', True)
|
||||
args = [
|
||||
daemon_bin,
|
||||
]
|
||||
add_datadir: bool = extra_config.get("add_datadir", True)
|
||||
if add_datadir:
|
||||
args.append('-datadir=' + datadir_path)
|
||||
args.append("-datadir=" + datadir_path)
|
||||
args += opts
|
||||
logger.info('Starting node {}'.format(daemon_bin))
|
||||
logger.debug('Arguments {}'.format(' '.join(args)))
|
||||
logger.info("Starting node {}".format(daemon_bin))
|
||||
logger.debug("Arguments {}".format(" ".join(args)))
|
||||
|
||||
opened_files = []
|
||||
if extra_config.get('stdout_to_file', False):
|
||||
stdout_dest = open(os.path.join(datadir_path, extra_config.get('stdout_filename', 'core_stdout.log')), 'w')
|
||||
if extra_config.get("stdout_to_file", False):
|
||||
stdout_dest = open(
|
||||
os.path.join(
|
||||
datadir_path, extra_config.get("stdout_filename", "core_stdout.log")
|
||||
),
|
||||
"w",
|
||||
)
|
||||
opened_files.append(stdout_dest)
|
||||
stderr_dest = stdout_dest
|
||||
else:
|
||||
|
@ -93,62 +101,113 @@ def startDaemon(node_dir, bin_dir, daemon_bin, opts=[], extra_config={}):
|
|||
stderr_dest = subprocess.PIPE
|
||||
|
||||
shell: bool = False
|
||||
if extra_config.get('use_shell', False):
|
||||
args = ' '.join(args)
|
||||
if extra_config.get("use_shell", False):
|
||||
args = " ".join(args)
|
||||
shell = True
|
||||
return Daemon(subprocess.Popen(args, shell=shell, stdin=subprocess.PIPE, stdout=stdout_dest, stderr=stderr_dest, cwd=datadir_path), opened_files)
|
||||
return Daemon(
|
||||
subprocess.Popen(
|
||||
args,
|
||||
shell=shell,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=stdout_dest,
|
||||
stderr=stderr_dest,
|
||||
cwd=datadir_path,
|
||||
),
|
||||
opened_files,
|
||||
)
|
||||
|
||||
|
||||
def startXmrDaemon(node_dir, bin_dir, daemon_bin, opts=[]):
|
||||
daemon_path = os.path.expanduser(os.path.join(bin_dir, daemon_bin))
|
||||
|
||||
datadir_path = os.path.expanduser(node_dir)
|
||||
config_filename = 'wownerod.conf' if daemon_bin.startswith('wow') else 'monerod.conf'
|
||||
args = [daemon_path, '--non-interactive', '--config-file=' + os.path.join(datadir_path, config_filename)] + opts
|
||||
logger.info('Starting node {}'.format(daemon_bin))
|
||||
logger.debug('Arguments {}'.format(' '.join(args)))
|
||||
config_filename = (
|
||||
"wownerod.conf" if daemon_bin.startswith("wow") else "monerod.conf"
|
||||
)
|
||||
args = [
|
||||
daemon_path,
|
||||
"--non-interactive",
|
||||
"--config-file=" + os.path.join(datadir_path, config_filename),
|
||||
] + opts
|
||||
logger.info("Starting node {}".format(daemon_bin))
|
||||
logger.debug("Arguments {}".format(" ".join(args)))
|
||||
|
||||
# return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
file_stdout = open(os.path.join(datadir_path, 'core_stdout.log'), 'w')
|
||||
file_stderr = open(os.path.join(datadir_path, 'core_stderr.log'), 'w')
|
||||
return Daemon(subprocess.Popen(args, stdin=subprocess.PIPE, stdout=file_stdout, stderr=file_stderr, cwd=datadir_path), [file_stdout, file_stderr])
|
||||
file_stdout = open(os.path.join(datadir_path, "core_stdout.log"), "w")
|
||||
file_stderr = open(os.path.join(datadir_path, "core_stderr.log"), "w")
|
||||
return Daemon(
|
||||
subprocess.Popen(
|
||||
args,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=file_stdout,
|
||||
stderr=file_stderr,
|
||||
cwd=datadir_path,
|
||||
),
|
||||
[file_stdout, file_stderr],
|
||||
)
|
||||
|
||||
|
||||
def startXmrWalletDaemon(node_dir, bin_dir, wallet_bin, opts=[]):
|
||||
daemon_path = os.path.expanduser(os.path.join(bin_dir, wallet_bin))
|
||||
args = [daemon_path, '--non-interactive']
|
||||
args = [daemon_path, "--non-interactive"]
|
||||
|
||||
needs_rewrite: bool = False
|
||||
config_to_remove = ['daemon-address=', 'untrusted-daemon=', 'trusted-daemon=', 'proxy=']
|
||||
config_to_remove = [
|
||||
"daemon-address=",
|
||||
"untrusted-daemon=",
|
||||
"trusted-daemon=",
|
||||
"proxy=",
|
||||
]
|
||||
|
||||
data_dir = os.path.expanduser(node_dir)
|
||||
|
||||
wallet_config_filename = 'wownero-wallet-rpc.conf' if wallet_bin.startswith('wow') else 'monero_wallet.conf'
|
||||
wallet_config_filename = (
|
||||
"wownero-wallet-rpc.conf"
|
||||
if wallet_bin.startswith("wow")
|
||||
else "monero_wallet.conf"
|
||||
)
|
||||
config_path = os.path.join(data_dir, wallet_config_filename)
|
||||
if os.path.exists(config_path):
|
||||
args += ['--config-file=' + config_path]
|
||||
args += ["--config-file=" + config_path]
|
||||
with open(config_path) as fp:
|
||||
for line in fp:
|
||||
if any(line.startswith(config_line) for config_line in config_to_remove):
|
||||
logger.warning('Found old config in monero_wallet.conf: {}'.format(line.strip()))
|
||||
if any(
|
||||
line.startswith(config_line) for config_line in config_to_remove
|
||||
):
|
||||
logger.warning(
|
||||
"Found old config in monero_wallet.conf: {}".format(
|
||||
line.strip()
|
||||
)
|
||||
)
|
||||
needs_rewrite = True
|
||||
args += opts
|
||||
|
||||
if needs_rewrite:
|
||||
logger.info('Rewriting wallet config')
|
||||
shutil.copyfile(config_path, config_path + '.last')
|
||||
with open(config_path + '.last') as fp_from, open(config_path, 'w') as fp_to:
|
||||
logger.info("Rewriting wallet config")
|
||||
shutil.copyfile(config_path, config_path + ".last")
|
||||
with open(config_path + ".last") as fp_from, open(config_path, "w") as fp_to:
|
||||
for line in fp_from:
|
||||
if not any(line.startswith(config_line) for config_line in config_to_remove):
|
||||
if not any(
|
||||
line.startswith(config_line) for config_line in config_to_remove
|
||||
):
|
||||
fp_to.write(line)
|
||||
|
||||
logger.info('Starting wallet daemon {}'.format(wallet_bin))
|
||||
logger.debug('Arguments {}'.format(' '.join(args)))
|
||||
logger.info("Starting wallet daemon {}".format(wallet_bin))
|
||||
logger.debug("Arguments {}".format(" ".join(args)))
|
||||
|
||||
# TODO: return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=data_dir)
|
||||
wallet_stdout = open(os.path.join(data_dir, 'wallet_stdout.log'), 'w')
|
||||
wallet_stderr = open(os.path.join(data_dir, 'wallet_stderr.log'), 'w')
|
||||
return Daemon(subprocess.Popen(args, stdin=subprocess.PIPE, stdout=wallet_stdout, stderr=wallet_stderr, cwd=data_dir), [wallet_stdout, wallet_stderr])
|
||||
wallet_stdout = open(os.path.join(data_dir, "wallet_stdout.log"), "w")
|
||||
wallet_stderr = open(os.path.join(data_dir, "wallet_stderr.log"), "w")
|
||||
return Daemon(
|
||||
subprocess.Popen(
|
||||
args,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=wallet_stdout,
|
||||
stderr=wallet_stderr,
|
||||
cwd=data_dir,
|
||||
),
|
||||
[wallet_stdout, wallet_stderr],
|
||||
)
|
||||
|
||||
|
||||
def ws_new_client(client, server):
|
||||
|
@ -165,25 +224,29 @@ def ws_client_left(client, server):
|
|||
|
||||
def ws_message_received(client, server, message):
|
||||
if len(message) > 200:
|
||||
message = message[:200] + '..'
|
||||
message = message[:200] + ".."
|
||||
if swap_client:
|
||||
swap_client.log.debug(f'ws_message_received {client["id"]} {message}')
|
||||
|
||||
|
||||
def getCoreBinName(coin_id: int, coin_settings, default_name: str) -> str:
|
||||
return coin_settings.get('core_binname', chainparams[coin_id].get('core_binname', default_name)) + ('.exe' if os.name == 'nt' else '')
|
||||
return coin_settings.get(
|
||||
"core_binname", chainparams[coin_id].get("core_binname", default_name)
|
||||
) + (".exe" if os.name == "nt" else "")
|
||||
|
||||
|
||||
def getWalletBinName(coin_id: int, coin_settings, default_name: str) -> str:
|
||||
return coin_settings.get('wallet_binname', chainparams[coin_id].get('wallet_binname', default_name)) + ('.exe' if os.name == 'nt' else '')
|
||||
return coin_settings.get(
|
||||
"wallet_binname", chainparams[coin_id].get("wallet_binname", default_name)
|
||||
) + (".exe" if os.name == "nt" else "")
|
||||
|
||||
|
||||
def getCoreBinArgs(coin_id: int, coin_settings):
|
||||
extra_args = []
|
||||
if 'config_filename' in coin_settings:
|
||||
extra_args.append('--conf=' + coin_settings['config_filename'])
|
||||
if 'port' in coin_settings:
|
||||
extra_args.append('--port=' + str(int(coin_settings['port'])))
|
||||
if "config_filename" in coin_settings:
|
||||
extra_args.append("--conf=" + coin_settings["config_filename"])
|
||||
if "port" in coin_settings:
|
||||
extra_args.append("--port=" + str(int(coin_settings["port"])))
|
||||
return extra_args
|
||||
|
||||
|
||||
|
@ -193,17 +256,21 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
|||
pids = []
|
||||
threads = []
|
||||
settings_path = os.path.join(data_dir, cfg.CONFIG_FILENAME)
|
||||
pids_path = os.path.join(data_dir, '.pids')
|
||||
pids_path = os.path.join(data_dir, ".pids")
|
||||
|
||||
if os.getenv('WALLET_ENCRYPTION_PWD', '') != '':
|
||||
if 'decred' in start_only_coins:
|
||||
if os.getenv("WALLET_ENCRYPTION_PWD", "") != "":
|
||||
if "decred" in start_only_coins:
|
||||
# Workaround for dcrwallet requiring password for initial startup
|
||||
logger.warning('Allowing set WALLET_ENCRYPTION_PWD var with --startonlycoin=decred.')
|
||||
logger.warning(
|
||||
"Allowing set WALLET_ENCRYPTION_PWD var with --startonlycoin=decred."
|
||||
)
|
||||
else:
|
||||
raise ValueError('Please unset the WALLET_ENCRYPTION_PWD environment variable.')
|
||||
raise ValueError(
|
||||
"Please unset the WALLET_ENCRYPTION_PWD environment variable."
|
||||
)
|
||||
|
||||
if not os.path.exists(settings_path):
|
||||
raise ValueError('Settings file not found: ' + str(settings_path))
|
||||
raise ValueError("Settings file not found: " + str(settings_path))
|
||||
|
||||
with open(settings_path) as fs:
|
||||
settings = json.load(fs)
|
||||
|
@ -215,7 +282,7 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
|||
with open(pids_path) as fd:
|
||||
for ln in fd:
|
||||
# TODO: try close
|
||||
logger.warning('Found pid for daemon {} '.format(ln.strip()))
|
||||
logger.warning("Found pid for daemon {} ".format(ln.strip()))
|
||||
|
||||
# Ensure daemons are stopped
|
||||
swap_client.stopDaemons()
|
||||
|
@ -224,155 +291,227 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
|||
settings = swap_client.settings
|
||||
try:
|
||||
# Try start daemons
|
||||
for c, v in settings['chainclients'].items():
|
||||
for c, v in settings["chainclients"].items():
|
||||
if len(start_only_coins) > 0 and c not in start_only_coins:
|
||||
continue
|
||||
try:
|
||||
coin_id = swap_client.getCoinIdFromName(c)
|
||||
display_name = getCoinName(coin_id)
|
||||
except Exception as e:
|
||||
logger.warning('Not starting unknown coin: {}'.format(c))
|
||||
except Exception as e: # noqa: F841
|
||||
logger.warning("Not starting unknown coin: {}".format(c))
|
||||
continue
|
||||
if c in ('monero', 'wownero'):
|
||||
if v['manage_daemon'] is True:
|
||||
swap_client.log.info(f'Starting {display_name} daemon')
|
||||
filename: str = getCoreBinName(coin_id, v, c + 'd')
|
||||
if c in ("monero", "wownero"):
|
||||
if v["manage_daemon"] is True:
|
||||
swap_client.log.info(f"Starting {display_name} daemon")
|
||||
filename: str = getCoreBinName(coin_id, v, c + "d")
|
||||
|
||||
daemons.append(startXmrDaemon(v['datadir'], v['bindir'], filename))
|
||||
daemons.append(startXmrDaemon(v["datadir"], v["bindir"], filename))
|
||||
pid = daemons[-1].handle.pid
|
||||
swap_client.log.info('Started {} {}'.format(filename, pid))
|
||||
swap_client.log.info("Started {} {}".format(filename, pid))
|
||||
|
||||
if v['manage_wallet_daemon'] is True:
|
||||
swap_client.log.info(f'Starting {display_name} wallet daemon')
|
||||
daemon_addr = '{}:{}'.format(v['rpchost'], v['rpcport'])
|
||||
trusted_daemon: bool = swap_client.getXMRTrustedDaemon(coin_id, v['rpchost'])
|
||||
opts = ['--daemon-address', daemon_addr, ]
|
||||
if v["manage_wallet_daemon"] is True:
|
||||
swap_client.log.info(f"Starting {display_name} wallet daemon")
|
||||
daemon_addr = "{}:{}".format(v["rpchost"], v["rpcport"])
|
||||
trusted_daemon: bool = swap_client.getXMRTrustedDaemon(
|
||||
coin_id, v["rpchost"]
|
||||
)
|
||||
opts = [
|
||||
"--daemon-address",
|
||||
daemon_addr,
|
||||
]
|
||||
|
||||
proxy_log_str = ''
|
||||
proxy_host, proxy_port = swap_client.getXMRWalletProxy(coin_id, v['rpchost'])
|
||||
proxy_log_str = ""
|
||||
proxy_host, proxy_port = swap_client.getXMRWalletProxy(
|
||||
coin_id, v["rpchost"]
|
||||
)
|
||||
if proxy_host:
|
||||
proxy_log_str = ' through proxy'
|
||||
opts += ['--proxy', f'{proxy_host}:{proxy_port}', '--daemon-ssl-allow-any-cert', ]
|
||||
proxy_log_str = " through proxy"
|
||||
opts += [
|
||||
"--proxy",
|
||||
f"{proxy_host}:{proxy_port}",
|
||||
"--daemon-ssl-allow-any-cert",
|
||||
]
|
||||
|
||||
swap_client.log.info('daemon-address: {} ({}){}'.format(daemon_addr, 'trusted' if trusted_daemon else 'untrusted', proxy_log_str))
|
||||
swap_client.log.info(
|
||||
"daemon-address: {} ({}){}".format(
|
||||
daemon_addr,
|
||||
"trusted" if trusted_daemon else "untrusted",
|
||||
proxy_log_str,
|
||||
)
|
||||
)
|
||||
|
||||
daemon_rpcuser = v.get('rpcuser', '')
|
||||
daemon_rpcpass = v.get('rpcpassword', '')
|
||||
if daemon_rpcuser != '':
|
||||
opts.append('--daemon-login')
|
||||
opts.append(daemon_rpcuser + ':' + daemon_rpcpass)
|
||||
daemon_rpcuser = v.get("rpcuser", "")
|
||||
daemon_rpcpass = v.get("rpcpassword", "")
|
||||
if daemon_rpcuser != "":
|
||||
opts.append("--daemon-login")
|
||||
opts.append(daemon_rpcuser + ":" + daemon_rpcpass)
|
||||
|
||||
opts.append('--trusted-daemon' if trusted_daemon else '--untrusted-daemon')
|
||||
filename: str = getWalletBinName(coin_id, v, c + '-wallet-rpc')
|
||||
opts.append(
|
||||
"--trusted-daemon" if trusted_daemon else "--untrusted-daemon"
|
||||
)
|
||||
filename: str = getWalletBinName(coin_id, v, c + "-wallet-rpc")
|
||||
|
||||
daemons.append(startXmrWalletDaemon(v['datadir'], v['bindir'], filename, opts))
|
||||
daemons.append(
|
||||
startXmrWalletDaemon(v["datadir"], v["bindir"], filename, opts)
|
||||
)
|
||||
pid = daemons[-1].handle.pid
|
||||
swap_client.log.info('Started {} {}'.format(filename, pid))
|
||||
swap_client.log.info("Started {} {}".format(filename, pid))
|
||||
|
||||
continue # /monero
|
||||
|
||||
if c == 'decred':
|
||||
appdata = v['datadir']
|
||||
extra_opts = [f'--appdata="{appdata}"', ]
|
||||
use_shell: bool = True if os.name == 'nt' else False
|
||||
if v['manage_daemon'] is True:
|
||||
swap_client.log.info(f'Starting {display_name} daemon')
|
||||
filename: str = getCoreBinName(coin_id, v, 'dcrd')
|
||||
if c == "decred":
|
||||
appdata = v["datadir"]
|
||||
extra_opts = [
|
||||
f'--appdata="{appdata}"',
|
||||
]
|
||||
use_shell: bool = True if os.name == "nt" else False
|
||||
if v["manage_daemon"] is True:
|
||||
swap_client.log.info(f"Starting {display_name} daemon")
|
||||
filename: str = getCoreBinName(coin_id, v, "dcrd")
|
||||
|
||||
extra_config = {'add_datadir': False, 'stdout_to_file': True, 'stdout_filename': 'dcrd_stdout.log', 'use_shell': use_shell}
|
||||
daemons.append(startDaemon(appdata, v['bindir'], filename, opts=extra_opts, extra_config=extra_config))
|
||||
extra_config = {
|
||||
"add_datadir": False,
|
||||
"stdout_to_file": True,
|
||||
"stdout_filename": "dcrd_stdout.log",
|
||||
"use_shell": use_shell,
|
||||
}
|
||||
daemons.append(
|
||||
startDaemon(
|
||||
appdata,
|
||||
v["bindir"],
|
||||
filename,
|
||||
opts=extra_opts,
|
||||
extra_config=extra_config,
|
||||
)
|
||||
)
|
||||
pid = daemons[-1].handle.pid
|
||||
swap_client.log.info('Started {} {}'.format(filename, pid))
|
||||
swap_client.log.info("Started {} {}".format(filename, pid))
|
||||
|
||||
if v['manage_wallet_daemon'] is True:
|
||||
swap_client.log.info(f'Starting {display_name} wallet daemon')
|
||||
filename: str = getWalletBinName(coin_id, v, 'dcrwallet')
|
||||
if v["manage_wallet_daemon"] is True:
|
||||
swap_client.log.info(f"Starting {display_name} wallet daemon")
|
||||
filename: str = getWalletBinName(coin_id, v, "dcrwallet")
|
||||
|
||||
wallet_pwd = v['wallet_pwd']
|
||||
if wallet_pwd == '':
|
||||
wallet_pwd = v["wallet_pwd"]
|
||||
if wallet_pwd == "":
|
||||
# Only set when in startonlycoin mode
|
||||
wallet_pwd = os.getenv('WALLET_ENCRYPTION_PWD', '')
|
||||
if wallet_pwd != '':
|
||||
wallet_pwd = os.getenv("WALLET_ENCRYPTION_PWD", "")
|
||||
if wallet_pwd != "":
|
||||
extra_opts.append(f'--pass="{wallet_pwd}"')
|
||||
extra_config = {'add_datadir': False, 'stdout_to_file': True, 'stdout_filename': 'dcrwallet_stdout.log', 'use_shell': use_shell}
|
||||
daemons.append(startDaemon(appdata, v['bindir'], filename, opts=extra_opts, extra_config=extra_config))
|
||||
extra_config = {
|
||||
"add_datadir": False,
|
||||
"stdout_to_file": True,
|
||||
"stdout_filename": "dcrwallet_stdout.log",
|
||||
"use_shell": use_shell,
|
||||
}
|
||||
daemons.append(
|
||||
startDaemon(
|
||||
appdata,
|
||||
v["bindir"],
|
||||
filename,
|
||||
opts=extra_opts,
|
||||
extra_config=extra_config,
|
||||
)
|
||||
)
|
||||
pid = daemons[-1].handle.pid
|
||||
swap_client.log.info('Started {} {}'.format(filename, pid))
|
||||
swap_client.log.info("Started {} {}".format(filename, pid))
|
||||
|
||||
continue # /decred
|
||||
|
||||
if v['manage_daemon'] is True:
|
||||
swap_client.log.info(f'Starting {display_name} daemon')
|
||||
if v["manage_daemon"] is True:
|
||||
swap_client.log.info(f"Starting {display_name} daemon")
|
||||
|
||||
filename: str = getCoreBinName(coin_id, v, c + 'd')
|
||||
filename: str = getCoreBinName(coin_id, v, c + "d")
|
||||
extra_opts = getCoreBinArgs(coin_id, v)
|
||||
daemons.append(startDaemon(v['datadir'], v['bindir'], filename, opts=extra_opts))
|
||||
daemons.append(
|
||||
startDaemon(v["datadir"], v["bindir"], filename, opts=extra_opts)
|
||||
)
|
||||
pid = daemons[-1].handle.pid
|
||||
pids.append((c, pid))
|
||||
swap_client.setDaemonPID(c, pid)
|
||||
swap_client.log.info('Started {} {}'.format(filename, pid))
|
||||
swap_client.log.info("Started {} {}".format(filename, pid))
|
||||
if len(pids) > 0:
|
||||
with open(pids_path, 'w') as fd:
|
||||
with open(pids_path, "w") as fd:
|
||||
for p in pids:
|
||||
fd.write('{}:{}\n'.format(*p))
|
||||
fd.write("{}:{}\n".format(*p))
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
|
||||
if len(start_only_coins) > 0:
|
||||
logger.info(f'Only running {start_only_coins}. Manually exit with Ctrl + c when ready.')
|
||||
logger.info(
|
||||
f"Only running {start_only_coins}. Manually exit with Ctrl + c when ready."
|
||||
)
|
||||
while not swap_client.delay_event.wait(0.5):
|
||||
pass
|
||||
else:
|
||||
swap_client.start()
|
||||
if 'htmlhost' in settings:
|
||||
swap_client.log.info('Starting http server at http://%s:%d.' % (settings['htmlhost'], settings['htmlport']))
|
||||
allow_cors = settings['allowcors'] if 'allowcors' in settings else cfg.DEFAULT_ALLOW_CORS
|
||||
thread_http = HttpThread(fp, settings['htmlhost'], settings['htmlport'], allow_cors, swap_client)
|
||||
if "htmlhost" in settings:
|
||||
swap_client.log.info(
|
||||
"Starting http server at http://%s:%d."
|
||||
% (settings["htmlhost"], settings["htmlport"])
|
||||
)
|
||||
allow_cors = (
|
||||
settings["allowcors"]
|
||||
if "allowcors" in settings
|
||||
else cfg.DEFAULT_ALLOW_CORS
|
||||
)
|
||||
thread_http = HttpThread(
|
||||
fp,
|
||||
settings["htmlhost"],
|
||||
settings["htmlport"],
|
||||
allow_cors,
|
||||
swap_client,
|
||||
)
|
||||
threads.append(thread_http)
|
||||
thread_http.start()
|
||||
|
||||
if 'wshost' in settings:
|
||||
ws_url = 'ws://{}:{}'.format(settings['wshost'], settings['wsport'])
|
||||
swap_client.log.info(f'Starting ws server at {ws_url}.')
|
||||
if "wshost" in settings:
|
||||
ws_url = "ws://{}:{}".format(settings["wshost"], settings["wsport"])
|
||||
swap_client.log.info(f"Starting ws server at {ws_url}.")
|
||||
|
||||
swap_client.ws_server = WebsocketServer(host=settings['wshost'], port=settings['wsport'])
|
||||
swap_client.ws_server.client_port = settings.get('wsclientport', settings['wsport'])
|
||||
swap_client.ws_server = WebsocketServer(
|
||||
host=settings["wshost"], port=settings["wsport"]
|
||||
)
|
||||
swap_client.ws_server.client_port = settings.get(
|
||||
"wsclientport", settings["wsport"]
|
||||
)
|
||||
swap_client.ws_server.set_fn_new_client(ws_new_client)
|
||||
swap_client.ws_server.set_fn_client_left(ws_client_left)
|
||||
swap_client.ws_server.set_fn_message_received(ws_message_received)
|
||||
swap_client.ws_server.run_forever(threaded=True)
|
||||
|
||||
logger.info('Exit with Ctrl + c.')
|
||||
logger.info("Exit with Ctrl + c.")
|
||||
while not swap_client.delay_event.wait(0.5):
|
||||
swap_client.update()
|
||||
|
||||
except Exception as ex:
|
||||
except Exception as e: # noqa: F841
|
||||
traceback.print_exc()
|
||||
|
||||
if swap_client.ws_server:
|
||||
try:
|
||||
swap_client.log.info('Stopping websocket server.')
|
||||
swap_client.log.info("Stopping websocket server.")
|
||||
swap_client.ws_server.shutdown_gracefully()
|
||||
except Exception as ex:
|
||||
except Exception as e: # noqa: F841
|
||||
traceback.print_exc()
|
||||
|
||||
swap_client.finalise()
|
||||
swap_client.log.info('Stopping HTTP threads.')
|
||||
swap_client.log.info("Stopping HTTP threads.")
|
||||
for t in threads:
|
||||
try:
|
||||
t.stop()
|
||||
t.join()
|
||||
except Exception as ex:
|
||||
except Exception as e: # noqa: F841
|
||||
traceback.print_exc()
|
||||
|
||||
closed_pids = []
|
||||
for d in daemons:
|
||||
swap_client.log.info('Interrupting {}'.format(d.handle.pid))
|
||||
swap_client.log.info("Interrupting {}".format(d.handle.pid))
|
||||
try:
|
||||
d.handle.send_signal(signal.CTRL_C_EVENT if os.name == 'nt' else signal.SIGINT)
|
||||
d.handle.send_signal(
|
||||
signal.CTRL_C_EVENT if os.name == "nt" else signal.SIGINT
|
||||
)
|
||||
except Exception as e:
|
||||
swap_client.log.info('Interrupting %d, error %s', d.handle.pid, str(e))
|
||||
swap_client.log.info("Interrupting %d, error %s", d.handle.pid, str(e))
|
||||
for d in daemons:
|
||||
try:
|
||||
d.handle.wait(timeout=120)
|
||||
|
@ -381,96 +520,106 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
|||
fp.close()
|
||||
closed_pids.append(d.handle.pid)
|
||||
except Exception as ex:
|
||||
swap_client.log.error('Error: {}'.format(ex))
|
||||
swap_client.log.error("Error: {}".format(ex))
|
||||
|
||||
if os.path.exists(pids_path):
|
||||
with open(pids_path) as fd:
|
||||
lines = fd.read().split('\n')
|
||||
still_running = ''
|
||||
lines = fd.read().split("\n")
|
||||
still_running = ""
|
||||
for ln in lines:
|
||||
try:
|
||||
if not int(ln.split(':')[1]) in closed_pids:
|
||||
still_running += ln + '\n'
|
||||
if int(ln.split(":")[1]) not in closed_pids:
|
||||
still_running += ln + "\n"
|
||||
except Exception:
|
||||
pass
|
||||
with open(pids_path, 'w') as fd:
|
||||
with open(pids_path, "w") as fd:
|
||||
fd.write(still_running)
|
||||
|
||||
|
||||
def printVersion():
|
||||
logger.info('Basicswap version: %s', __version__)
|
||||
logger.info("Basicswap version: %s", __version__)
|
||||
|
||||
|
||||
def printHelp():
|
||||
print('Usage: basicswap-run ')
|
||||
print('\n--help, -h Print help.')
|
||||
print('--version, -v Print version.')
|
||||
print('--datadir=PATH Path to basicswap data directory, default:{}.'.format(cfg.BASICSWAP_DATADIR))
|
||||
print('--mainnet Run in mainnet mode.')
|
||||
print('--testnet Run in testnet mode.')
|
||||
print('--regtest Run in regtest mode.')
|
||||
print('--startonlycoin Only start the provides coin daemon/s, use this if a chain requires extra processing.')
|
||||
print("Usage: basicswap-run ")
|
||||
print("\n--help, -h Print help.")
|
||||
print("--version, -v Print version.")
|
||||
print(
|
||||
"--datadir=PATH Path to basicswap data directory, default:{}.".format(
|
||||
cfg.BASICSWAP_DATADIR
|
||||
)
|
||||
)
|
||||
print("--mainnet Run in mainnet mode.")
|
||||
print("--testnet Run in testnet mode.")
|
||||
print("--regtest Run in regtest mode.")
|
||||
print(
|
||||
"--startonlycoin Only start the provides coin daemon/s, use this if a chain requires extra processing."
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
data_dir = None
|
||||
chain = 'mainnet'
|
||||
chain = "mainnet"
|
||||
start_only_coins = set()
|
||||
|
||||
for v in sys.argv[1:]:
|
||||
if len(v) < 2 or v[0] != '-':
|
||||
logger.warning('Unknown argument %s', v)
|
||||
if len(v) < 2 or v[0] != "-":
|
||||
logger.warning("Unknown argument %s", v)
|
||||
continue
|
||||
|
||||
s = v.split('=')
|
||||
s = v.split("=")
|
||||
name = s[0].strip()
|
||||
|
||||
for i in range(2):
|
||||
if name[0] == '-':
|
||||
if name[0] == "-":
|
||||
name = name[1:]
|
||||
|
||||
if name == 'v' or name == 'version':
|
||||
if name == "v" or name == "version":
|
||||
printVersion()
|
||||
return 0
|
||||
if name == 'h' or name == 'help':
|
||||
if name == "h" or name == "help":
|
||||
printHelp()
|
||||
return 0
|
||||
|
||||
if name in ('mainnet', 'testnet', 'regtest'):
|
||||
if name in ("mainnet", "testnet", "regtest"):
|
||||
chain = name
|
||||
continue
|
||||
|
||||
if len(s) == 2:
|
||||
if name == 'datadir':
|
||||
if name == "datadir":
|
||||
data_dir = os.path.expanduser(s[1])
|
||||
continue
|
||||
if name == 'startonlycoin':
|
||||
for coin in [s.lower() for s in s[1].split(',')]:
|
||||
if name == "startonlycoin":
|
||||
for coin in [s.lower() for s in s[1].split(",")]:
|
||||
if is_known_coin(coin) is False:
|
||||
raise ValueError(f'Unknown coin: {coin}')
|
||||
raise ValueError(f"Unknown coin: {coin}")
|
||||
start_only_coins.add(coin)
|
||||
continue
|
||||
|
||||
logger.warning('Unknown argument %s', v)
|
||||
logger.warning("Unknown argument %s", v)
|
||||
|
||||
if os.name == 'nt':
|
||||
logger.warning('Running on windows is discouraged and windows support may be discontinued in the future. Please consider using the WSL docker setup instead.')
|
||||
if os.name == "nt":
|
||||
logger.warning(
|
||||
"Running on windows is discouraged and windows support may be discontinued in the future. Please consider using the WSL docker setup instead."
|
||||
)
|
||||
|
||||
if data_dir is None:
|
||||
data_dir = os.path.join(os.path.expanduser(cfg.BASICSWAP_DATADIR))
|
||||
logger.info('Using datadir: %s', data_dir)
|
||||
logger.info('Chain: %s', chain)
|
||||
logger.info("Using datadir: %s", data_dir)
|
||||
logger.info("Chain: %s", 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')
|
||||
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, start_only_coins)
|
||||
|
||||
logger.info('Done.')
|
||||
logger.info("Done.")
|
||||
return swap_client.fail_code if swap_client is not None else 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -9,8 +9,8 @@ from .util import (
|
|||
COIN,
|
||||
)
|
||||
|
||||
XMR_COIN = 10 ** 12
|
||||
WOW_COIN = 10 ** 11
|
||||
XMR_COIN = 10**12
|
||||
WOW_COIN = 10**11
|
||||
|
||||
|
||||
class Coins(IntEnum):
|
||||
|
@ -35,459 +35,459 @@ class Coins(IntEnum):
|
|||
|
||||
chainparams = {
|
||||
Coins.PART: {
|
||||
'name': 'particl',
|
||||
'ticker': 'PART',
|
||||
'message_magic': 'Bitcoin Signed Message:\n',
|
||||
'blocks_target': 60 * 2,
|
||||
'decimal_places': 8,
|
||||
'mainnet': {
|
||||
'rpcport': 51735,
|
||||
'pubkey_address': 0x38,
|
||||
'script_address': 0x3c,
|
||||
'key_prefix': 0x6c,
|
||||
'stealth_key_prefix': 0x14,
|
||||
'hrp': 'pw',
|
||||
'bip44': 44,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
"name": "particl",
|
||||
"ticker": "PART",
|
||||
"message_magic": "Bitcoin Signed Message:\n",
|
||||
"blocks_target": 60 * 2,
|
||||
"decimal_places": 8,
|
||||
"mainnet": {
|
||||
"rpcport": 51735,
|
||||
"pubkey_address": 0x38,
|
||||
"script_address": 0x3C,
|
||||
"key_prefix": 0x6C,
|
||||
"stealth_key_prefix": 0x14,
|
||||
"hrp": "pw",
|
||||
"bip44": 44,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'testnet': {
|
||||
'rpcport': 51935,
|
||||
'pubkey_address': 0x76,
|
||||
'script_address': 0x7a,
|
||||
'key_prefix': 0x2e,
|
||||
'stealth_key_prefix': 0x15,
|
||||
'hrp': 'tpw',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
"testnet": {
|
||||
"rpcport": 51935,
|
||||
"pubkey_address": 0x76,
|
||||
"script_address": 0x7A,
|
||||
"key_prefix": 0x2E,
|
||||
"stealth_key_prefix": 0x15,
|
||||
"hrp": "tpw",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
"regtest": {
|
||||
"rpcport": 51936,
|
||||
"pubkey_address": 0x76,
|
||||
"script_address": 0x7A,
|
||||
"key_prefix": 0x2E,
|
||||
"stealth_key_prefix": 0x15,
|
||||
"hrp": "rtpw",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'regtest': {
|
||||
'rpcport': 51936,
|
||||
'pubkey_address': 0x76,
|
||||
'script_address': 0x7a,
|
||||
'key_prefix': 0x2e,
|
||||
'stealth_key_prefix': 0x15,
|
||||
'hrp': 'rtpw',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
}
|
||||
},
|
||||
Coins.BTC: {
|
||||
'name': 'bitcoin',
|
||||
'ticker': 'BTC',
|
||||
'message_magic': 'Bitcoin Signed Message:\n',
|
||||
'blocks_target': 60 * 10,
|
||||
'decimal_places': 8,
|
||||
'mainnet': {
|
||||
'rpcport': 8332,
|
||||
'pubkey_address': 0,
|
||||
'script_address': 5,
|
||||
'key_prefix': 128,
|
||||
'hrp': 'bc',
|
||||
'bip44': 0,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
"name": "bitcoin",
|
||||
"ticker": "BTC",
|
||||
"message_magic": "Bitcoin Signed Message:\n",
|
||||
"blocks_target": 60 * 10,
|
||||
"decimal_places": 8,
|
||||
"mainnet": {
|
||||
"rpcport": 8332,
|
||||
"pubkey_address": 0,
|
||||
"script_address": 5,
|
||||
"key_prefix": 128,
|
||||
"hrp": "bc",
|
||||
"bip44": 0,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'testnet': {
|
||||
'rpcport': 18332,
|
||||
'pubkey_address': 111,
|
||||
'script_address': 196,
|
||||
'key_prefix': 239,
|
||||
'hrp': 'tb',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
'name': 'testnet3',
|
||||
"testnet": {
|
||||
"rpcport": 18332,
|
||||
"pubkey_address": 111,
|
||||
"script_address": 196,
|
||||
"key_prefix": 239,
|
||||
"hrp": "tb",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
"name": "testnet3",
|
||||
},
|
||||
"regtest": {
|
||||
"rpcport": 18443,
|
||||
"pubkey_address": 111,
|
||||
"script_address": 196,
|
||||
"key_prefix": 239,
|
||||
"hrp": "bcrt",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'regtest': {
|
||||
'rpcport': 18443,
|
||||
'pubkey_address': 111,
|
||||
'script_address': 196,
|
||||
'key_prefix': 239,
|
||||
'hrp': 'bcrt',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
}
|
||||
},
|
||||
Coins.LTC: {
|
||||
'name': 'litecoin',
|
||||
'ticker': 'LTC',
|
||||
'message_magic': 'Litecoin Signed Message:\n',
|
||||
'blocks_target': 60 * 1,
|
||||
'decimal_places': 8,
|
||||
'mainnet': {
|
||||
'rpcport': 9332,
|
||||
'pubkey_address': 48,
|
||||
'script_address': 5,
|
||||
'script_address2': 50,
|
||||
'key_prefix': 176,
|
||||
'hrp': 'ltc',
|
||||
'bip44': 2,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
"name": "litecoin",
|
||||
"ticker": "LTC",
|
||||
"message_magic": "Litecoin Signed Message:\n",
|
||||
"blocks_target": 60 * 1,
|
||||
"decimal_places": 8,
|
||||
"mainnet": {
|
||||
"rpcport": 9332,
|
||||
"pubkey_address": 48,
|
||||
"script_address": 5,
|
||||
"script_address2": 50,
|
||||
"key_prefix": 176,
|
||||
"hrp": "ltc",
|
||||
"bip44": 2,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'testnet': {
|
||||
'rpcport': 19332,
|
||||
'pubkey_address': 111,
|
||||
'script_address': 196,
|
||||
'script_address2': 58,
|
||||
'key_prefix': 239,
|
||||
'hrp': 'tltc',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
'name': 'testnet4',
|
||||
"testnet": {
|
||||
"rpcport": 19332,
|
||||
"pubkey_address": 111,
|
||||
"script_address": 196,
|
||||
"script_address2": 58,
|
||||
"key_prefix": 239,
|
||||
"hrp": "tltc",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
"name": "testnet4",
|
||||
},
|
||||
"regtest": {
|
||||
"rpcport": 19443,
|
||||
"pubkey_address": 111,
|
||||
"script_address": 196,
|
||||
"script_address2": 58,
|
||||
"key_prefix": 239,
|
||||
"hrp": "rltc",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'regtest': {
|
||||
'rpcport': 19443,
|
||||
'pubkey_address': 111,
|
||||
'script_address': 196,
|
||||
'script_address2': 58,
|
||||
'key_prefix': 239,
|
||||
'hrp': 'rltc',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
}
|
||||
},
|
||||
Coins.DCR: {
|
||||
'name': 'decred',
|
||||
'ticker': 'DCR',
|
||||
'message_magic': 'Decred Signed Message:\n',
|
||||
'blocks_target': 60 * 5,
|
||||
'decimal_places': 8,
|
||||
'mainnet': {
|
||||
'rpcport': 9109,
|
||||
'pubkey_address': 0x073f,
|
||||
'script_address': 0x071a,
|
||||
'key_prefix': 0x22de,
|
||||
'bip44': 42,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
"name": "decred",
|
||||
"ticker": "DCR",
|
||||
"message_magic": "Decred Signed Message:\n",
|
||||
"blocks_target": 60 * 5,
|
||||
"decimal_places": 8,
|
||||
"mainnet": {
|
||||
"rpcport": 9109,
|
||||
"pubkey_address": 0x073F,
|
||||
"script_address": 0x071A,
|
||||
"key_prefix": 0x22DE,
|
||||
"bip44": 42,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'testnet': {
|
||||
'rpcport': 19109,
|
||||
'pubkey_address': 0x0f21,
|
||||
'script_address': 0x0efc,
|
||||
'key_prefix': 0x230e,
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
'name': 'testnet3',
|
||||
"testnet": {
|
||||
"rpcport": 19109,
|
||||
"pubkey_address": 0x0F21,
|
||||
"script_address": 0x0EFC,
|
||||
"key_prefix": 0x230E,
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
"name": "testnet3",
|
||||
},
|
||||
"regtest": { # simnet
|
||||
"rpcport": 18656,
|
||||
"pubkey_address": 0x0E91,
|
||||
"script_address": 0x0E6C,
|
||||
"key_prefix": 0x2307,
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'regtest': { # simnet
|
||||
'rpcport': 18656,
|
||||
'pubkey_address': 0x0e91,
|
||||
'script_address': 0x0e6c,
|
||||
'key_prefix': 0x2307,
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
}
|
||||
},
|
||||
Coins.NMC: {
|
||||
'name': 'namecoin',
|
||||
'ticker': 'NMC',
|
||||
'message_magic': 'Namecoin Signed Message:\n',
|
||||
'blocks_target': 60 * 10,
|
||||
'decimal_places': 8,
|
||||
'mainnet': {
|
||||
'rpcport': 8336,
|
||||
'pubkey_address': 52,
|
||||
'script_address': 13,
|
||||
'hrp': 'nc',
|
||||
'bip44': 7,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
"name": "namecoin",
|
||||
"ticker": "NMC",
|
||||
"message_magic": "Namecoin Signed Message:\n",
|
||||
"blocks_target": 60 * 10,
|
||||
"decimal_places": 8,
|
||||
"mainnet": {
|
||||
"rpcport": 8336,
|
||||
"pubkey_address": 52,
|
||||
"script_address": 13,
|
||||
"hrp": "nc",
|
||||
"bip44": 7,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'testnet': {
|
||||
'rpcport': 18336,
|
||||
'pubkey_address': 111,
|
||||
'script_address': 196,
|
||||
'hrp': 'tn',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
'name': 'testnet3',
|
||||
"testnet": {
|
||||
"rpcport": 18336,
|
||||
"pubkey_address": 111,
|
||||
"script_address": 196,
|
||||
"hrp": "tn",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
"name": "testnet3",
|
||||
},
|
||||
"regtest": {
|
||||
"rpcport": 18443,
|
||||
"pubkey_address": 111,
|
||||
"script_address": 196,
|
||||
"hrp": "ncrt",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'regtest': {
|
||||
'rpcport': 18443,
|
||||
'pubkey_address': 111,
|
||||
'script_address': 196,
|
||||
'hrp': 'ncrt',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
}
|
||||
},
|
||||
Coins.XMR: {
|
||||
'name': 'monero',
|
||||
'ticker': 'XMR',
|
||||
'client': 'xmr',
|
||||
'decimal_places': 12,
|
||||
'mainnet': {
|
||||
'rpcport': 18081,
|
||||
'walletrpcport': 18082,
|
||||
'min_amount': 100000,
|
||||
'max_amount': 10000 * XMR_COIN,
|
||||
'address_prefix': 18,
|
||||
"name": "monero",
|
||||
"ticker": "XMR",
|
||||
"client": "xmr",
|
||||
"decimal_places": 12,
|
||||
"mainnet": {
|
||||
"rpcport": 18081,
|
||||
"walletrpcport": 18082,
|
||||
"min_amount": 100000,
|
||||
"max_amount": 10000 * XMR_COIN,
|
||||
"address_prefix": 18,
|
||||
},
|
||||
'testnet': {
|
||||
'rpcport': 28081,
|
||||
'walletrpcport': 28082,
|
||||
'min_amount': 100000,
|
||||
'max_amount': 10000 * XMR_COIN,
|
||||
'address_prefix': 18,
|
||||
"testnet": {
|
||||
"rpcport": 28081,
|
||||
"walletrpcport": 28082,
|
||||
"min_amount": 100000,
|
||||
"max_amount": 10000 * XMR_COIN,
|
||||
"address_prefix": 18,
|
||||
},
|
||||
"regtest": {
|
||||
"rpcport": 18081,
|
||||
"walletrpcport": 18082,
|
||||
"min_amount": 100000,
|
||||
"max_amount": 10000 * XMR_COIN,
|
||||
"address_prefix": 18,
|
||||
},
|
||||
'regtest': {
|
||||
'rpcport': 18081,
|
||||
'walletrpcport': 18082,
|
||||
'min_amount': 100000,
|
||||
'max_amount': 10000 * XMR_COIN,
|
||||
'address_prefix': 18,
|
||||
}
|
||||
},
|
||||
Coins.WOW: {
|
||||
'name': 'wownero',
|
||||
'ticker': 'WOW',
|
||||
'client': 'wow',
|
||||
'decimal_places': 11,
|
||||
'mainnet': {
|
||||
'rpcport': 34568,
|
||||
'walletrpcport': 34572, # todo
|
||||
'min_amount': 100000,
|
||||
'max_amount': 10000 * WOW_COIN,
|
||||
'address_prefix': 4146,
|
||||
"name": "wownero",
|
||||
"ticker": "WOW",
|
||||
"client": "wow",
|
||||
"decimal_places": 11,
|
||||
"mainnet": {
|
||||
"rpcport": 34568,
|
||||
"walletrpcport": 34572, # todo
|
||||
"min_amount": 100000,
|
||||
"max_amount": 10000 * WOW_COIN,
|
||||
"address_prefix": 4146,
|
||||
},
|
||||
'testnet': {
|
||||
'rpcport': 44568,
|
||||
'walletrpcport': 44572,
|
||||
'min_amount': 100000,
|
||||
'max_amount': 10000 * WOW_COIN,
|
||||
'address_prefix': 4146,
|
||||
"testnet": {
|
||||
"rpcport": 44568,
|
||||
"walletrpcport": 44572,
|
||||
"min_amount": 100000,
|
||||
"max_amount": 10000 * WOW_COIN,
|
||||
"address_prefix": 4146,
|
||||
},
|
||||
"regtest": {
|
||||
"rpcport": 54568,
|
||||
"walletrpcport": 54572,
|
||||
"min_amount": 100000,
|
||||
"max_amount": 10000 * WOW_COIN,
|
||||
"address_prefix": 4146,
|
||||
},
|
||||
'regtest': {
|
||||
'rpcport': 54568,
|
||||
'walletrpcport': 54572,
|
||||
'min_amount': 100000,
|
||||
'max_amount': 10000 * WOW_COIN,
|
||||
'address_prefix': 4146,
|
||||
}
|
||||
},
|
||||
Coins.PIVX: {
|
||||
'name': 'pivx',
|
||||
'ticker': 'PIVX',
|
||||
'display_name': 'PIVX',
|
||||
'message_magic': 'DarkNet Signed Message:\n',
|
||||
'blocks_target': 60 * 1,
|
||||
'decimal_places': 8,
|
||||
'has_cltv': True,
|
||||
'has_csv': False,
|
||||
'has_segwit': False,
|
||||
'mainnet': {
|
||||
'rpcport': 51473,
|
||||
'pubkey_address': 30,
|
||||
'script_address': 13,
|
||||
'key_prefix': 212,
|
||||
'bip44': 119,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
"name": "pivx",
|
||||
"ticker": "PIVX",
|
||||
"display_name": "PIVX",
|
||||
"message_magic": "DarkNet Signed Message:\n",
|
||||
"blocks_target": 60 * 1,
|
||||
"decimal_places": 8,
|
||||
"has_cltv": True,
|
||||
"has_csv": False,
|
||||
"has_segwit": False,
|
||||
"mainnet": {
|
||||
"rpcport": 51473,
|
||||
"pubkey_address": 30,
|
||||
"script_address": 13,
|
||||
"key_prefix": 212,
|
||||
"bip44": 119,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'testnet': {
|
||||
'rpcport': 51475,
|
||||
'pubkey_address': 139,
|
||||
'script_address': 19,
|
||||
'key_prefix': 239,
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
'name': 'testnet4',
|
||||
"testnet": {
|
||||
"rpcport": 51475,
|
||||
"pubkey_address": 139,
|
||||
"script_address": 19,
|
||||
"key_prefix": 239,
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
"name": "testnet4",
|
||||
},
|
||||
"regtest": {
|
||||
"rpcport": 51477,
|
||||
"pubkey_address": 139,
|
||||
"script_address": 19,
|
||||
"key_prefix": 239,
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'regtest': {
|
||||
'rpcport': 51477,
|
||||
'pubkey_address': 139,
|
||||
'script_address': 19,
|
||||
'key_prefix': 239,
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
}
|
||||
},
|
||||
Coins.DASH: {
|
||||
'name': 'dash',
|
||||
'ticker': 'DASH',
|
||||
'message_magic': 'DarkCoin Signed Message:\n',
|
||||
'blocks_target': 60 * 2.5,
|
||||
'decimal_places': 8,
|
||||
'has_csv': True,
|
||||
'has_segwit': False,
|
||||
'mainnet': {
|
||||
'rpcport': 9998,
|
||||
'pubkey_address': 76,
|
||||
'script_address': 16,
|
||||
'key_prefix': 204,
|
||||
'hrp': '',
|
||||
'bip44': 5,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
"name": "dash",
|
||||
"ticker": "DASH",
|
||||
"message_magic": "DarkCoin Signed Message:\n",
|
||||
"blocks_target": 60 * 2.5,
|
||||
"decimal_places": 8,
|
||||
"has_csv": True,
|
||||
"has_segwit": False,
|
||||
"mainnet": {
|
||||
"rpcport": 9998,
|
||||
"pubkey_address": 76,
|
||||
"script_address": 16,
|
||||
"key_prefix": 204,
|
||||
"hrp": "",
|
||||
"bip44": 5,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'testnet': {
|
||||
'rpcport': 19998,
|
||||
'pubkey_address': 140,
|
||||
'script_address': 19,
|
||||
'key_prefix': 239,
|
||||
'hrp': '',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
"testnet": {
|
||||
"rpcport": 19998,
|
||||
"pubkey_address": 140,
|
||||
"script_address": 19,
|
||||
"key_prefix": 239,
|
||||
"hrp": "",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
"regtest": {
|
||||
"rpcport": 18332,
|
||||
"pubkey_address": 140,
|
||||
"script_address": 19,
|
||||
"key_prefix": 239,
|
||||
"hrp": "",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'regtest': {
|
||||
'rpcport': 18332,
|
||||
'pubkey_address': 140,
|
||||
'script_address': 19,
|
||||
'key_prefix': 239,
|
||||
'hrp': '',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
}
|
||||
},
|
||||
Coins.FIRO: {
|
||||
'name': 'firo',
|
||||
'ticker': 'FIRO',
|
||||
'message_magic': 'Zcoin Signed Message:\n',
|
||||
'blocks_target': 60 * 10,
|
||||
'decimal_places': 8,
|
||||
'has_cltv': False,
|
||||
'has_csv': False,
|
||||
'has_segwit': False,
|
||||
'mainnet': {
|
||||
'rpcport': 8888,
|
||||
'pubkey_address': 82,
|
||||
'script_address': 7,
|
||||
'key_prefix': 210,
|
||||
'hrp': '',
|
||||
'bip44': 136,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
"name": "firo",
|
||||
"ticker": "FIRO",
|
||||
"message_magic": "Zcoin Signed Message:\n",
|
||||
"blocks_target": 60 * 10,
|
||||
"decimal_places": 8,
|
||||
"has_cltv": False,
|
||||
"has_csv": False,
|
||||
"has_segwit": False,
|
||||
"mainnet": {
|
||||
"rpcport": 8888,
|
||||
"pubkey_address": 82,
|
||||
"script_address": 7,
|
||||
"key_prefix": 210,
|
||||
"hrp": "",
|
||||
"bip44": 136,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'testnet': {
|
||||
'rpcport': 18888,
|
||||
'pubkey_address': 65,
|
||||
'script_address': 178,
|
||||
'key_prefix': 185,
|
||||
'hrp': '',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
"testnet": {
|
||||
"rpcport": 18888,
|
||||
"pubkey_address": 65,
|
||||
"script_address": 178,
|
||||
"key_prefix": 185,
|
||||
"hrp": "",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
"regtest": {
|
||||
"rpcport": 28888,
|
||||
"pubkey_address": 65,
|
||||
"script_address": 178,
|
||||
"key_prefix": 239,
|
||||
"hrp": "",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'regtest': {
|
||||
'rpcport': 28888,
|
||||
'pubkey_address': 65,
|
||||
'script_address': 178,
|
||||
'key_prefix': 239,
|
||||
'hrp': '',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
}
|
||||
},
|
||||
Coins.NAV: {
|
||||
'name': 'navcoin',
|
||||
'ticker': 'NAV',
|
||||
'message_magic': 'Navcoin Signed Message:\n',
|
||||
'blocks_target': 30,
|
||||
'decimal_places': 8,
|
||||
'has_csv': True,
|
||||
'has_segwit': True,
|
||||
'mainnet': {
|
||||
'rpcport': 44444,
|
||||
'pubkey_address': 53,
|
||||
'script_address': 85,
|
||||
'key_prefix': 150,
|
||||
'hrp': '',
|
||||
'bip44': 130,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
"name": "navcoin",
|
||||
"ticker": "NAV",
|
||||
"message_magic": "Navcoin Signed Message:\n",
|
||||
"blocks_target": 30,
|
||||
"decimal_places": 8,
|
||||
"has_csv": True,
|
||||
"has_segwit": True,
|
||||
"mainnet": {
|
||||
"rpcport": 44444,
|
||||
"pubkey_address": 53,
|
||||
"script_address": 85,
|
||||
"key_prefix": 150,
|
||||
"hrp": "",
|
||||
"bip44": 130,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'testnet': {
|
||||
'rpcport': 44445,
|
||||
'pubkey_address': 111,
|
||||
'script_address': 196,
|
||||
'key_prefix': 239,
|
||||
'hrp': '',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
"testnet": {
|
||||
"rpcport": 44445,
|
||||
"pubkey_address": 111,
|
||||
"script_address": 196,
|
||||
"key_prefix": 239,
|
||||
"hrp": "",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
"regtest": {
|
||||
"rpcport": 44446,
|
||||
"pubkey_address": 111,
|
||||
"script_address": 196,
|
||||
"key_prefix": 239,
|
||||
"hrp": "",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'regtest': {
|
||||
'rpcport': 44446,
|
||||
'pubkey_address': 111,
|
||||
'script_address': 196,
|
||||
'key_prefix': 239,
|
||||
'hrp': '',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
}
|
||||
},
|
||||
Coins.BCH: {
|
||||
'name': 'bitcoincash',
|
||||
'ticker': 'BCH',
|
||||
'display_name': 'Bitcoin Cash',
|
||||
'message_magic': 'Bitcoin Signed Message:\n',
|
||||
'blocks_target': 60 * 2,
|
||||
'decimal_places': 8,
|
||||
'has_cltv': True,
|
||||
'has_csv': True,
|
||||
'has_segwit': False,
|
||||
'cli_binname': 'bitcoin-cli',
|
||||
'core_binname': 'bitcoind',
|
||||
'mainnet': {
|
||||
'rpcport': 8332,
|
||||
'pubkey_address': 0,
|
||||
'script_address': 5,
|
||||
'key_prefix': 128,
|
||||
'hrp': 'bitcoincash',
|
||||
'bip44': 0,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
"name": "bitcoincash",
|
||||
"ticker": "BCH",
|
||||
"display_name": "Bitcoin Cash",
|
||||
"message_magic": "Bitcoin Signed Message:\n",
|
||||
"blocks_target": 60 * 2,
|
||||
"decimal_places": 8,
|
||||
"has_cltv": True,
|
||||
"has_csv": True,
|
||||
"has_segwit": False,
|
||||
"cli_binname": "bitcoin-cli",
|
||||
"core_binname": "bitcoind",
|
||||
"mainnet": {
|
||||
"rpcport": 8332,
|
||||
"pubkey_address": 0,
|
||||
"script_address": 5,
|
||||
"key_prefix": 128,
|
||||
"hrp": "bitcoincash",
|
||||
"bip44": 0,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'testnet': {
|
||||
'rpcport': 18332,
|
||||
'pubkey_address': 111,
|
||||
'script_address': 196,
|
||||
'key_prefix': 239,
|
||||
'hrp': 'bchtest',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
'name': 'testnet3',
|
||||
"testnet": {
|
||||
"rpcport": 18332,
|
||||
"pubkey_address": 111,
|
||||
"script_address": 196,
|
||||
"key_prefix": 239,
|
||||
"hrp": "bchtest",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
"name": "testnet3",
|
||||
},
|
||||
"regtest": {
|
||||
"rpcport": 18443,
|
||||
"pubkey_address": 111,
|
||||
"script_address": 196,
|
||||
"key_prefix": 239,
|
||||
"hrp": "bchreg",
|
||||
"bip44": 1,
|
||||
"min_amount": 1000,
|
||||
"max_amount": 100000 * COIN,
|
||||
},
|
||||
'regtest': {
|
||||
'rpcport': 18443,
|
||||
'pubkey_address': 111,
|
||||
'script_address': 196,
|
||||
'key_prefix': 239,
|
||||
'hrp': 'bchreg',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
}
|
||||
},
|
||||
}
|
||||
ticker_map = {}
|
||||
|
||||
|
||||
for c, params in chainparams.items():
|
||||
ticker_map[params['ticker'].lower()] = c
|
||||
ticker_map[params["ticker"].lower()] = c
|
||||
|
||||
|
||||
def getCoinIdFromTicker(ticker: str) -> str:
|
||||
try:
|
||||
return ticker_map[ticker.lower()]
|
||||
except Exception:
|
||||
raise ValueError('Unknown coin')
|
||||
raise ValueError("Unknown coin")
|
||||
|
|
|
@ -6,35 +6,47 @@
|
|||
|
||||
import os
|
||||
|
||||
CONFIG_FILENAME = 'basicswap.json'
|
||||
BASICSWAP_DATADIR = os.getenv('BASICSWAP_DATADIR', os.path.join('~', '.basicswap'))
|
||||
CONFIG_FILENAME = "basicswap.json"
|
||||
BASICSWAP_DATADIR = os.getenv("BASICSWAP_DATADIR", os.path.join("~", ".basicswap"))
|
||||
DEFAULT_ALLOW_CORS = False
|
||||
TEST_DATADIRS = os.path.expanduser(os.getenv('DATADIRS', '/tmp/basicswap'))
|
||||
DEFAULT_TEST_BINDIR = os.path.expanduser(os.getenv('DEFAULT_TEST_BINDIR', os.path.join('~', '.basicswap', 'bin')))
|
||||
TEST_DATADIRS = os.path.expanduser(os.getenv("DATADIRS", "/tmp/basicswap"))
|
||||
DEFAULT_TEST_BINDIR = os.path.expanduser(
|
||||
os.getenv("DEFAULT_TEST_BINDIR", os.path.join("~", ".basicswap", "bin"))
|
||||
)
|
||||
|
||||
bin_suffix = ('.exe' if os.name == 'nt' else '')
|
||||
PARTICL_BINDIR = os.path.expanduser(os.getenv('PARTICL_BINDIR', os.path.join(DEFAULT_TEST_BINDIR, 'particl')))
|
||||
PARTICLD = os.getenv('PARTICLD', 'particld' + bin_suffix)
|
||||
PARTICL_CLI = os.getenv('PARTICL_CLI', 'particl-cli' + bin_suffix)
|
||||
PARTICL_TX = os.getenv('PARTICL_TX', 'particl-tx' + bin_suffix)
|
||||
bin_suffix = ".exe" if os.name == "nt" else ""
|
||||
PARTICL_BINDIR = os.path.expanduser(
|
||||
os.getenv("PARTICL_BINDIR", os.path.join(DEFAULT_TEST_BINDIR, "particl"))
|
||||
)
|
||||
PARTICLD = os.getenv("PARTICLD", "particld" + bin_suffix)
|
||||
PARTICL_CLI = os.getenv("PARTICL_CLI", "particl-cli" + bin_suffix)
|
||||
PARTICL_TX = os.getenv("PARTICL_TX", "particl-tx" + bin_suffix)
|
||||
|
||||
BITCOIN_BINDIR = os.path.expanduser(os.getenv('BITCOIN_BINDIR', os.path.join(DEFAULT_TEST_BINDIR, 'bitcoin')))
|
||||
BITCOIND = os.getenv('BITCOIND', 'bitcoind' + bin_suffix)
|
||||
BITCOIN_CLI = os.getenv('BITCOIN_CLI', 'bitcoin-cli' + bin_suffix)
|
||||
BITCOIN_TX = os.getenv('BITCOIN_TX', 'bitcoin-tx' + bin_suffix)
|
||||
BITCOIN_BINDIR = os.path.expanduser(
|
||||
os.getenv("BITCOIN_BINDIR", os.path.join(DEFAULT_TEST_BINDIR, "bitcoin"))
|
||||
)
|
||||
BITCOIND = os.getenv("BITCOIND", "bitcoind" + bin_suffix)
|
||||
BITCOIN_CLI = os.getenv("BITCOIN_CLI", "bitcoin-cli" + bin_suffix)
|
||||
BITCOIN_TX = os.getenv("BITCOIN_TX", "bitcoin-tx" + bin_suffix)
|
||||
|
||||
LITECOIN_BINDIR = os.path.expanduser(os.getenv('LITECOIN_BINDIR', os.path.join(DEFAULT_TEST_BINDIR, 'litecoin')))
|
||||
LITECOIND = os.getenv('LITECOIND', 'litecoind' + bin_suffix)
|
||||
LITECOIN_CLI = os.getenv('LITECOIN_CLI', 'litecoin-cli' + bin_suffix)
|
||||
LITECOIN_TX = os.getenv('LITECOIN_TX', 'litecoin-tx' + bin_suffix)
|
||||
LITECOIN_BINDIR = os.path.expanduser(
|
||||
os.getenv("LITECOIN_BINDIR", os.path.join(DEFAULT_TEST_BINDIR, "litecoin"))
|
||||
)
|
||||
LITECOIND = os.getenv("LITECOIND", "litecoind" + bin_suffix)
|
||||
LITECOIN_CLI = os.getenv("LITECOIN_CLI", "litecoin-cli" + bin_suffix)
|
||||
LITECOIN_TX = os.getenv("LITECOIN_TX", "litecoin-tx" + bin_suffix)
|
||||
|
||||
NAMECOIN_BINDIR = os.path.expanduser(os.getenv('NAMECOIN_BINDIR', os.path.join(DEFAULT_TEST_BINDIR, 'namecoin')))
|
||||
NAMECOIND = os.getenv('NAMECOIND', 'namecoind' + bin_suffix)
|
||||
NAMECOIN_CLI = os.getenv('NAMECOIN_CLI', 'namecoin-cli' + bin_suffix)
|
||||
NAMECOIN_TX = os.getenv('NAMECOIN_TX', 'namecoin-tx' + bin_suffix)
|
||||
NAMECOIN_BINDIR = os.path.expanduser(
|
||||
os.getenv("NAMECOIN_BINDIR", os.path.join(DEFAULT_TEST_BINDIR, "namecoin"))
|
||||
)
|
||||
NAMECOIND = os.getenv("NAMECOIND", "namecoind" + bin_suffix)
|
||||
NAMECOIN_CLI = os.getenv("NAMECOIN_CLI", "namecoin-cli" + bin_suffix)
|
||||
NAMECOIN_TX = os.getenv("NAMECOIN_TX", "namecoin-tx" + bin_suffix)
|
||||
|
||||
XMR_BINDIR = os.path.expanduser(os.getenv('XMR_BINDIR', os.path.join(DEFAULT_TEST_BINDIR, 'monero')))
|
||||
XMRD = os.getenv('XMRD', 'monerod' + bin_suffix)
|
||||
XMR_WALLET_RPC = os.getenv('XMR_WALLET_RPC', 'monero-wallet-rpc' + bin_suffix)
|
||||
XMR_BINDIR = os.path.expanduser(
|
||||
os.getenv("XMR_BINDIR", os.path.join(DEFAULT_TEST_BINDIR, "monero"))
|
||||
)
|
||||
XMRD = os.getenv("XMRD", "monerod" + bin_suffix)
|
||||
XMR_WALLET_RPC = os.getenv("XMR_WALLET_RPC", "monero-wallet-rpc" + bin_suffix)
|
||||
|
||||
# NOTE: Adding coin definitions here is deprecated. Please add in coin test file.
|
||||
|
|
118
basicswap/db.py
118
basicswap/db.py
|
@ -25,34 +25,34 @@ class Concepts(IntEnum):
|
|||
|
||||
def strConcepts(state):
|
||||
if state == Concepts.OFFER:
|
||||
return 'Offer'
|
||||
return "Offer"
|
||||
if state == Concepts.BID:
|
||||
return 'Bid'
|
||||
return "Bid"
|
||||
if state == Concepts.NETWORK_MESSAGE:
|
||||
return 'Network Message'
|
||||
return 'Unknown'
|
||||
return "Network Message"
|
||||
return "Unknown"
|
||||
|
||||
|
||||
def pack_state(new_state: int, now: int) -> bytes:
|
||||
return int(new_state).to_bytes(4, 'little') + now.to_bytes(8, 'little')
|
||||
return int(new_state).to_bytes(4, "little") + now.to_bytes(8, "little")
|
||||
|
||||
|
||||
class DBKVInt(Base):
|
||||
__tablename__ = 'kv_int'
|
||||
__tablename__ = "kv_int"
|
||||
|
||||
key = sa.Column(sa.String, primary_key=True)
|
||||
value = sa.Column(sa.Integer)
|
||||
|
||||
|
||||
class DBKVString(Base):
|
||||
__tablename__ = 'kv_string'
|
||||
__tablename__ = "kv_string"
|
||||
|
||||
key = sa.Column(sa.String, primary_key=True)
|
||||
value = sa.Column(sa.String)
|
||||
|
||||
|
||||
class Offer(Base):
|
||||
__tablename__ = 'offers'
|
||||
__tablename__ = "offers"
|
||||
|
||||
offer_id = sa.Column(sa.LargeBinary, primary_key=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
@ -89,7 +89,9 @@ class Offer(Base):
|
|||
|
||||
# Local fields
|
||||
auto_accept_bids = sa.Column(sa.Boolean)
|
||||
withdraw_to_addr = sa.Column(sa.String) # Address to spend lock tx to - address from wallet if empty TODO
|
||||
withdraw_to_addr = sa.Column(
|
||||
sa.String
|
||||
) # Address to spend lock tx to - address from wallet if empty TODO
|
||||
security_token = sa.Column(sa.LargeBinary)
|
||||
bid_reversed = sa.Column(sa.Boolean)
|
||||
|
||||
|
@ -106,10 +108,10 @@ class Offer(Base):
|
|||
|
||||
|
||||
class Bid(Base):
|
||||
__tablename__ = 'bids'
|
||||
__tablename__ = "bids"
|
||||
|
||||
bid_id = sa.Column(sa.LargeBinary, primary_key=True)
|
||||
offer_id = sa.Column(sa.LargeBinary, sa.ForeignKey('offers.offer_id'))
|
||||
offer_id = sa.Column(sa.LargeBinary, sa.ForeignKey("offers.offer_id"))
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
||||
protocol_version = sa.Column(sa.Integer)
|
||||
|
@ -121,13 +123,17 @@ class Bid(Base):
|
|||
bid_addr = sa.Column(sa.String)
|
||||
proof_address = sa.Column(sa.String)
|
||||
proof_utxos = sa.Column(sa.LargeBinary)
|
||||
withdraw_to_addr = sa.Column(sa.String) # Address to spend lock tx to - address from wallet if empty TODO
|
||||
withdraw_to_addr = sa.Column(
|
||||
sa.String
|
||||
) # Address to spend lock tx to - address from wallet if empty TODO
|
||||
|
||||
recovered_secret = sa.Column(sa.LargeBinary)
|
||||
amount_to = sa.Column(sa.BigInteger) # amount * offer.rate
|
||||
|
||||
pkhash_buyer = sa.Column(sa.LargeBinary)
|
||||
pkhash_buyer_to = sa.Column(sa.LargeBinary) # Used for the ptx if coin pubkey hashes differ
|
||||
pkhash_buyer_to = sa.Column(
|
||||
sa.LargeBinary
|
||||
) # Used for the ptx if coin pubkey hashes differ
|
||||
amount = sa.Column(sa.BigInteger)
|
||||
rate = sa.Column(sa.BigInteger)
|
||||
|
||||
|
@ -149,8 +155,12 @@ class Bid(Base):
|
|||
debug_ind = sa.Column(sa.Integer)
|
||||
security_token = sa.Column(sa.LargeBinary)
|
||||
|
||||
chain_a_height_start = sa.Column(sa.Integer) # Height of script chain before the swap
|
||||
chain_b_height_start = sa.Column(sa.Integer) # Height of scriptless chain before the swap
|
||||
chain_a_height_start = sa.Column(
|
||||
sa.Integer
|
||||
) # Height of script chain before the swap
|
||||
chain_b_height_start = sa.Column(
|
||||
sa.Integer
|
||||
) # Height of scriptless chain before the swap
|
||||
|
||||
reject_code = sa.Column(sa.Integer)
|
||||
|
||||
|
@ -199,12 +209,12 @@ class Bid(Base):
|
|||
|
||||
|
||||
class SwapTx(Base):
|
||||
__tablename__ = 'transactions'
|
||||
__tablename__ = "transactions"
|
||||
|
||||
bid_id = sa.Column(sa.LargeBinary, sa.ForeignKey('bids.bid_id'))
|
||||
bid_id = sa.Column(sa.LargeBinary, sa.ForeignKey("bids.bid_id"))
|
||||
tx_type = sa.Column(sa.Integer) # TxTypes
|
||||
__table_args__ = (
|
||||
sa.PrimaryKeyConstraint('bid_id', 'tx_type'),
|
||||
sa.PrimaryKeyConstraint("bid_id", "tx_type"),
|
||||
{},
|
||||
)
|
||||
|
||||
|
@ -240,7 +250,7 @@ class SwapTx(Base):
|
|||
|
||||
|
||||
class PrefundedTx(Base):
|
||||
__tablename__ = 'prefunded_transactions'
|
||||
__tablename__ = "prefunded_transactions"
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
@ -253,7 +263,7 @@ class PrefundedTx(Base):
|
|||
|
||||
|
||||
class PooledAddress(Base):
|
||||
__tablename__ = 'addresspool'
|
||||
__tablename__ = "addresspool"
|
||||
|
||||
addr_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
coin_type = sa.Column(sa.Integer)
|
||||
|
@ -263,13 +273,13 @@ class PooledAddress(Base):
|
|||
|
||||
|
||||
class SentOffer(Base):
|
||||
__tablename__ = 'sentoffers'
|
||||
__tablename__ = "sentoffers"
|
||||
|
||||
offer_id = sa.Column(sa.LargeBinary, primary_key=True)
|
||||
|
||||
|
||||
class SmsgAddress(Base):
|
||||
__tablename__ = 'smsgaddresses'
|
||||
__tablename__ = "smsgaddresses"
|
||||
|
||||
addr_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
@ -281,7 +291,7 @@ class SmsgAddress(Base):
|
|||
|
||||
|
||||
class Action(Base):
|
||||
__tablename__ = 'actions'
|
||||
__tablename__ = "actions"
|
||||
|
||||
action_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
@ -293,7 +303,7 @@ class Action(Base):
|
|||
|
||||
|
||||
class EventLog(Base):
|
||||
__tablename__ = 'eventlog'
|
||||
__tablename__ = "eventlog"
|
||||
|
||||
event_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
@ -303,32 +313,38 @@ class EventLog(Base):
|
|||
event_type = sa.Column(sa.Integer)
|
||||
event_msg = sa.Column(sa.String)
|
||||
|
||||
__table_args__ = (sa.Index('main_index', 'linked_type', 'linked_id'), )
|
||||
__table_args__ = (sa.Index("main_index", "linked_type", "linked_id"),)
|
||||
|
||||
|
||||
class XmrOffer(Base):
|
||||
__tablename__ = 'xmr_offers'
|
||||
__tablename__ = "xmr_offers"
|
||||
# TODO: Merge to Offer
|
||||
|
||||
swap_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
offer_id = sa.Column(sa.LargeBinary, sa.ForeignKey('offers.offer_id'))
|
||||
offer_id = sa.Column(sa.LargeBinary, sa.ForeignKey("offers.offer_id"))
|
||||
|
||||
a_fee_rate = sa.Column(sa.BigInteger) # Chain a fee rate
|
||||
b_fee_rate = sa.Column(sa.BigInteger) # Chain b fee rate
|
||||
|
||||
lock_time_1 = sa.Column(sa.Integer) # Delay before the chain a lock refund tx can be mined
|
||||
lock_time_2 = sa.Column(sa.Integer) # Delay before the follower can spend from the chain a lock refund tx
|
||||
lock_time_1 = sa.Column(
|
||||
sa.Integer
|
||||
) # Delay before the chain a lock refund tx can be mined
|
||||
lock_time_2 = sa.Column(
|
||||
sa.Integer
|
||||
) # Delay before the follower can spend from the chain a lock refund tx
|
||||
|
||||
|
||||
class XmrSwap(Base):
|
||||
__tablename__ = 'xmr_swaps'
|
||||
__tablename__ = "xmr_swaps"
|
||||
|
||||
swap_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
bid_id = sa.Column(sa.LargeBinary, sa.ForeignKey('bids.bid_id'))
|
||||
bid_id = sa.Column(sa.LargeBinary, sa.ForeignKey("bids.bid_id"))
|
||||
|
||||
contract_count = sa.Column(sa.Integer)
|
||||
|
||||
dest_af = sa.Column(sa.LargeBinary) # Destination for coin A amount to follower when swap completes successfully
|
||||
dest_af = sa.Column(
|
||||
sa.LargeBinary
|
||||
) # Destination for coin A amount to follower when swap completes successfully
|
||||
|
||||
pkal = sa.Column(sa.LargeBinary)
|
||||
pkasl = sa.Column(sa.LargeBinary)
|
||||
|
@ -349,9 +365,9 @@ class XmrSwap(Base):
|
|||
kbsl_dleag = sa.Column(sa.LargeBinary)
|
||||
kbsf_dleag = sa.Column(sa.LargeBinary)
|
||||
|
||||
vkbv = sa.Column(sa.LargeBinary) # chain b view private key
|
||||
pkbv = sa.Column(sa.LargeBinary) # chain b view public key
|
||||
pkbs = sa.Column(sa.LargeBinary) # chain b spend public key
|
||||
vkbv = sa.Column(sa.LargeBinary) # chain b view private key
|
||||
pkbv = sa.Column(sa.LargeBinary) # chain b view public key
|
||||
pkbs = sa.Column(sa.LargeBinary) # chain b spend public key
|
||||
|
||||
a_lock_tx = sa.Column(sa.LargeBinary)
|
||||
a_lock_tx_script = sa.Column(sa.LargeBinary)
|
||||
|
@ -376,13 +392,15 @@ class XmrSwap(Base):
|
|||
al_lock_spend_tx_esig = sa.Column(sa.LargeBinary)
|
||||
kal_sig = sa.Column(sa.LargeBinary)
|
||||
|
||||
a_lock_refund_swipe_tx = sa.Column(sa.LargeBinary) # Follower spends script coin lock refund tx
|
||||
a_lock_refund_swipe_tx = sa.Column(
|
||||
sa.LargeBinary
|
||||
) # Follower spends script coin lock refund tx
|
||||
|
||||
b_lock_tx_id = sa.Column(sa.LargeBinary)
|
||||
|
||||
|
||||
class XmrSplitData(Base):
|
||||
__tablename__ = 'xmr_split_data'
|
||||
__tablename__ = "xmr_split_data"
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
addr_from = sa.Column(sa.String)
|
||||
|
@ -393,11 +411,13 @@ class XmrSplitData(Base):
|
|||
dleag = sa.Column(sa.LargeBinary)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
|
||||
__table_args__ = (sa.UniqueConstraint('bid_id', 'msg_type', 'msg_sequence', name='uc_1'),)
|
||||
__table_args__ = (
|
||||
sa.UniqueConstraint("bid_id", "msg_type", "msg_sequence", name="uc_1"),
|
||||
)
|
||||
|
||||
|
||||
class RevokedMessage(Base):
|
||||
__tablename__ = 'revoked_messages'
|
||||
__tablename__ = "revoked_messages"
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
@ -407,7 +427,7 @@ class RevokedMessage(Base):
|
|||
|
||||
|
||||
class Wallets(Base):
|
||||
__tablename__ = 'wallets'
|
||||
__tablename__ = "wallets"
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
@ -419,7 +439,7 @@ class Wallets(Base):
|
|||
|
||||
|
||||
class KnownIdentity(Base):
|
||||
__tablename__ = 'knownidentities'
|
||||
__tablename__ = "knownidentities"
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
@ -441,7 +461,7 @@ class KnownIdentity(Base):
|
|||
|
||||
|
||||
class AutomationStrategy(Base):
|
||||
__tablename__ = 'automationstrategies'
|
||||
__tablename__ = "automationstrategies"
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
@ -457,7 +477,7 @@ class AutomationStrategy(Base):
|
|||
|
||||
|
||||
class AutomationLink(Base):
|
||||
__tablename__ = 'automationlinks'
|
||||
__tablename__ = "automationlinks"
|
||||
# Contains per order/bid options
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
|
@ -474,11 +494,11 @@ class AutomationLink(Base):
|
|||
note = sa.Column(sa.String)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
|
||||
__table_args__ = (sa.Index('linked_index', 'linked_type', 'linked_id'), )
|
||||
__table_args__ = (sa.Index("linked_index", "linked_type", "linked_id"),)
|
||||
|
||||
|
||||
class History(Base):
|
||||
__tablename__ = 'history'
|
||||
__tablename__ = "history"
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
concept_type = sa.Column(sa.Integer)
|
||||
|
@ -489,7 +509,7 @@ class History(Base):
|
|||
|
||||
|
||||
class BidState(Base):
|
||||
__tablename__ = 'bidstates'
|
||||
__tablename__ = "bidstates"
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
@ -505,7 +525,7 @@ class BidState(Base):
|
|||
|
||||
|
||||
class Notification(Base):
|
||||
__tablename__ = 'notifications'
|
||||
__tablename__ = "notifications"
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
@ -515,7 +535,7 @@ class Notification(Base):
|
|||
|
||||
|
||||
class MessageLink(Base):
|
||||
__tablename__ = 'message_links'
|
||||
__tablename__ = "message_links"
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
@ -531,7 +551,7 @@ class MessageLink(Base):
|
|||
|
||||
|
||||
class CheckedBlock(Base):
|
||||
__tablename__ = 'checkedblocks'
|
||||
__tablename__ = "checkedblocks"
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
|
|
|
@ -14,7 +14,8 @@ from .db import (
|
|||
Concepts,
|
||||
AutomationStrategy,
|
||||
CURRENT_DB_VERSION,
|
||||
CURRENT_DB_DATA_VERSION)
|
||||
CURRENT_DB_DATA_VERSION,
|
||||
)
|
||||
|
||||
from .basicswap_util import (
|
||||
BidStates,
|
||||
|
@ -30,7 +31,11 @@ def upgradeDatabaseData(self, data_version):
|
|||
if data_version >= CURRENT_DB_DATA_VERSION:
|
||||
return
|
||||
|
||||
self.log.info('Upgrading database records from version %d to %d.', data_version, CURRENT_DB_DATA_VERSION)
|
||||
self.log.info(
|
||||
"Upgrading database records from version %d to %d.",
|
||||
data_version,
|
||||
CURRENT_DB_DATA_VERSION,
|
||||
)
|
||||
with self.mxDB:
|
||||
try:
|
||||
session = scoped_session(self.session_factory)
|
||||
|
@ -38,65 +43,100 @@ def upgradeDatabaseData(self, data_version):
|
|||
now = int(time.time())
|
||||
|
||||
if data_version < 1:
|
||||
session.add(AutomationStrategy(
|
||||
active_ind=1,
|
||||
label='Accept All',
|
||||
type_ind=Concepts.OFFER,
|
||||
data=json.dumps({'exact_rate_only': True,
|
||||
'max_concurrent_bids': 5}).encode('utf-8'),
|
||||
only_known_identities=False,
|
||||
created_at=now))
|
||||
session.add(AutomationStrategy(
|
||||
active_ind=1,
|
||||
label='Accept Known',
|
||||
type_ind=Concepts.OFFER,
|
||||
data=json.dumps({'exact_rate_only': True,
|
||||
'max_concurrent_bids': 5}).encode('utf-8'),
|
||||
only_known_identities=True,
|
||||
note='Accept bids from identities with previously successful swaps only',
|
||||
created_at=now))
|
||||
session.add(
|
||||
AutomationStrategy(
|
||||
active_ind=1,
|
||||
label="Accept All",
|
||||
type_ind=Concepts.OFFER,
|
||||
data=json.dumps(
|
||||
{"exact_rate_only": True, "max_concurrent_bids": 5}
|
||||
).encode("utf-8"),
|
||||
only_known_identities=False,
|
||||
created_at=now,
|
||||
)
|
||||
)
|
||||
session.add(
|
||||
AutomationStrategy(
|
||||
active_ind=1,
|
||||
label="Accept Known",
|
||||
type_ind=Concepts.OFFER,
|
||||
data=json.dumps(
|
||||
{"exact_rate_only": True, "max_concurrent_bids": 5}
|
||||
).encode("utf-8"),
|
||||
only_known_identities=True,
|
||||
note="Accept bids from identities with previously successful swaps only",
|
||||
created_at=now,
|
||||
)
|
||||
)
|
||||
|
||||
for state in BidStates:
|
||||
session.add(BidState(
|
||||
active_ind=1,
|
||||
state_id=int(state),
|
||||
in_progress=isActiveBidState(state),
|
||||
in_error=isErrorBidState(state),
|
||||
swap_failed=isFailingBidState(state),
|
||||
swap_ended=isFinalBidState(state),
|
||||
label=strBidState(state),
|
||||
created_at=now))
|
||||
session.add(
|
||||
BidState(
|
||||
active_ind=1,
|
||||
state_id=int(state),
|
||||
in_progress=isActiveBidState(state),
|
||||
in_error=isErrorBidState(state),
|
||||
swap_failed=isFailingBidState(state),
|
||||
swap_ended=isFinalBidState(state),
|
||||
label=strBidState(state),
|
||||
created_at=now,
|
||||
)
|
||||
)
|
||||
|
||||
if data_version > 0 and 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))
|
||||
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,
|
||||
)
|
||||
)
|
||||
if data_version > 0 and data_version < 3:
|
||||
for state in BidStates:
|
||||
in_error = isErrorBidState(state)
|
||||
swap_failed = isFailingBidState(state)
|
||||
swap_ended = isFinalBidState(state)
|
||||
session.execute(text('UPDATE bidstates SET in_error = :in_error, swap_failed = :swap_failed, swap_ended = :swap_ended WHERE state_id = :state_id', {'in_error': in_error, 'swap_failed': swap_failed, 'swap_ended': swap_ended, 'state_id': int(state)}))
|
||||
session.execute(
|
||||
text(
|
||||
"UPDATE bidstates SET in_error = :in_error, swap_failed = :swap_failed, swap_ended = :swap_ended WHERE state_id = :state_id",
|
||||
{
|
||||
"in_error": in_error,
|
||||
"swap_failed": swap_failed,
|
||||
"swap_ended": swap_ended,
|
||||
"state_id": int(state),
|
||||
},
|
||||
)
|
||||
)
|
||||
if data_version > 0 and data_version < 4:
|
||||
for state in (BidStates.BID_REQUEST_SENT, BidStates.BID_REQUEST_ACCEPTED):
|
||||
session.add(BidState(
|
||||
active_ind=1,
|
||||
state_id=int(state),
|
||||
in_progress=isActiveBidState(state),
|
||||
in_error=isErrorBidState(state),
|
||||
swap_failed=isFailingBidState(state),
|
||||
swap_ended=isFinalBidState(state),
|
||||
label=strBidState(state),
|
||||
created_at=now))
|
||||
for state in (
|
||||
BidStates.BID_REQUEST_SENT,
|
||||
BidStates.BID_REQUEST_ACCEPTED,
|
||||
):
|
||||
session.add(
|
||||
BidState(
|
||||
active_ind=1,
|
||||
state_id=int(state),
|
||||
in_progress=isActiveBidState(state),
|
||||
in_error=isErrorBidState(state),
|
||||
swap_failed=isFailingBidState(state),
|
||||
swap_ended=isFinalBidState(state),
|
||||
label=strBidState(state),
|
||||
created_at=now,
|
||||
)
|
||||
)
|
||||
|
||||
self.db_data_version = CURRENT_DB_DATA_VERSION
|
||||
self.setIntKV('db_data_version', self.db_data_version, session)
|
||||
self.setIntKV("db_data_version", self.db_data_version, session)
|
||||
session.commit()
|
||||
self.log.info('Upgraded database records to version {}'.format(self.db_data_version))
|
||||
self.log.info(
|
||||
"Upgraded database records to version {}".format(self.db_data_version)
|
||||
)
|
||||
finally:
|
||||
session.close()
|
||||
session.remove()
|
||||
|
@ -106,23 +146,31 @@ def upgradeDatabase(self, db_version):
|
|||
if db_version >= CURRENT_DB_VERSION:
|
||||
return
|
||||
|
||||
self.log.info('Upgrading database from version %d to %d.', db_version, CURRENT_DB_VERSION)
|
||||
self.log.info(
|
||||
"Upgrading database from version %d to %d.", db_version, CURRENT_DB_VERSION
|
||||
)
|
||||
|
||||
while True:
|
||||
session = scoped_session(self.session_factory)
|
||||
|
||||
current_version = db_version
|
||||
if current_version == 6:
|
||||
session.execute(text('ALTER TABLE bids ADD COLUMN security_token BLOB'))
|
||||
session.execute(text('ALTER TABLE offers ADD COLUMN security_token BLOB'))
|
||||
session.execute(text("ALTER TABLE bids ADD COLUMN security_token BLOB"))
|
||||
session.execute(text("ALTER TABLE offers ADD COLUMN security_token BLOB"))
|
||||
db_version += 1
|
||||
elif current_version == 7:
|
||||
session.execute(text('ALTER TABLE transactions ADD COLUMN block_hash BLOB'))
|
||||
session.execute(text('ALTER TABLE transactions ADD COLUMN block_height INTEGER'))
|
||||
session.execute(text('ALTER TABLE transactions ADD COLUMN block_time INTEGER'))
|
||||
session.execute(text("ALTER TABLE transactions ADD COLUMN block_hash BLOB"))
|
||||
session.execute(
|
||||
text("ALTER TABLE transactions ADD COLUMN block_height INTEGER")
|
||||
)
|
||||
session.execute(
|
||||
text("ALTER TABLE transactions ADD COLUMN block_time INTEGER")
|
||||
)
|
||||
db_version += 1
|
||||
elif current_version == 8:
|
||||
session.execute(text('''
|
||||
session.execute(
|
||||
text(
|
||||
"""
|
||||
CREATE TABLE wallets (
|
||||
record_id INTEGER NOT NULL,
|
||||
coin_id INTEGER,
|
||||
|
@ -130,30 +178,48 @@ def upgradeDatabase(self, db_version):
|
|||
wallet_data VARCHAR,
|
||||
balance_type INTEGER,
|
||||
created_at BIGINT,
|
||||
PRIMARY KEY (record_id))'''))
|
||||
PRIMARY KEY (record_id))"""
|
||||
)
|
||||
)
|
||||
db_version += 1
|
||||
elif current_version == 9:
|
||||
session.execute(text('ALTER TABLE wallets ADD COLUMN wallet_data VARCHAR'))
|
||||
session.execute(text("ALTER TABLE wallets ADD COLUMN wallet_data VARCHAR"))
|
||||
db_version += 1
|
||||
elif current_version == 10:
|
||||
session.execute(text('ALTER TABLE smsgaddresses ADD COLUMN active_ind INTEGER'))
|
||||
session.execute(text('ALTER TABLE smsgaddresses ADD COLUMN created_at INTEGER'))
|
||||
session.execute(text('ALTER TABLE smsgaddresses ADD COLUMN note VARCHAR'))
|
||||
session.execute(text('ALTER TABLE smsgaddresses ADD COLUMN pubkey VARCHAR'))
|
||||
session.execute(text('UPDATE smsgaddresses SET active_ind = 1, created_at = 1'))
|
||||
session.execute(
|
||||
text("ALTER TABLE smsgaddresses ADD COLUMN active_ind INTEGER")
|
||||
)
|
||||
session.execute(
|
||||
text("ALTER TABLE smsgaddresses ADD COLUMN created_at INTEGER")
|
||||
)
|
||||
session.execute(text("ALTER TABLE smsgaddresses ADD COLUMN note VARCHAR"))
|
||||
session.execute(text("ALTER TABLE smsgaddresses ADD COLUMN pubkey VARCHAR"))
|
||||
session.execute(
|
||||
text("UPDATE smsgaddresses SET active_ind = 1, created_at = 1")
|
||||
)
|
||||
|
||||
session.execute(text('ALTER TABLE offers ADD COLUMN addr_to VARCHAR'))
|
||||
session.execute(text("ALTER TABLE offers ADD COLUMN addr_to VARCHAR"))
|
||||
session.execute(text(f'UPDATE offers SET addr_to = "{self.network_addr}"'))
|
||||
db_version += 1
|
||||
elif current_version == 11:
|
||||
session.execute(text('ALTER TABLE bids ADD COLUMN chain_a_height_start INTEGER'))
|
||||
session.execute(text('ALTER TABLE bids ADD COLUMN chain_b_height_start INTEGER'))
|
||||
session.execute(text('ALTER TABLE bids ADD COLUMN protocol_version INTEGER'))
|
||||
session.execute(text('ALTER TABLE offers ADD COLUMN protocol_version INTEGER'))
|
||||
session.execute(text('ALTER TABLE transactions ADD COLUMN tx_data BLOB'))
|
||||
session.execute(
|
||||
text("ALTER TABLE bids ADD COLUMN chain_a_height_start INTEGER")
|
||||
)
|
||||
session.execute(
|
||||
text("ALTER TABLE bids ADD COLUMN chain_b_height_start INTEGER")
|
||||
)
|
||||
session.execute(
|
||||
text("ALTER TABLE bids ADD COLUMN protocol_version INTEGER")
|
||||
)
|
||||
session.execute(
|
||||
text("ALTER TABLE offers ADD COLUMN protocol_version INTEGER")
|
||||
)
|
||||
session.execute(text("ALTER TABLE transactions ADD COLUMN tx_data BLOB"))
|
||||
db_version += 1
|
||||
elif current_version == 12:
|
||||
session.execute(text('''
|
||||
session.execute(
|
||||
text(
|
||||
"""
|
||||
CREATE TABLE knownidentities (
|
||||
record_id INTEGER NOT NULL,
|
||||
address VARCHAR,
|
||||
|
@ -168,15 +234,23 @@ def upgradeDatabase(self, db_version):
|
|||
note VARCHAR,
|
||||
updated_at BIGINT,
|
||||
created_at BIGINT,
|
||||
PRIMARY KEY (record_id))'''))
|
||||
session.execute(text('ALTER TABLE bids ADD COLUMN reject_code INTEGER'))
|
||||
session.execute(text('ALTER TABLE bids ADD COLUMN rate INTEGER'))
|
||||
session.execute(text('ALTER TABLE offers ADD COLUMN amount_negotiable INTEGER'))
|
||||
session.execute(text('ALTER TABLE offers ADD COLUMN rate_negotiable INTEGER'))
|
||||
PRIMARY KEY (record_id))"""
|
||||
)
|
||||
)
|
||||
session.execute(text("ALTER TABLE bids ADD COLUMN reject_code INTEGER"))
|
||||
session.execute(text("ALTER TABLE bids ADD COLUMN rate INTEGER"))
|
||||
session.execute(
|
||||
text("ALTER TABLE offers ADD COLUMN amount_negotiable INTEGER")
|
||||
)
|
||||
session.execute(
|
||||
text("ALTER TABLE offers ADD COLUMN rate_negotiable INTEGER")
|
||||
)
|
||||
db_version += 1
|
||||
elif current_version == 13:
|
||||
db_version += 1
|
||||
session.execute(text('''
|
||||
session.execute(
|
||||
text(
|
||||
"""
|
||||
CREATE TABLE automationstrategies (
|
||||
record_id INTEGER NOT NULL,
|
||||
active_ind INTEGER,
|
||||
|
@ -188,9 +262,13 @@ def upgradeDatabase(self, db_version):
|
|||
|
||||
note VARCHAR,
|
||||
created_at BIGINT,
|
||||
PRIMARY KEY (record_id))'''))
|
||||
PRIMARY KEY (record_id))"""
|
||||
)
|
||||
)
|
||||
|
||||
session.execute(text('''
|
||||
session.execute(
|
||||
text(
|
||||
"""
|
||||
CREATE TABLE automationlinks (
|
||||
record_id INTEGER NOT NULL,
|
||||
active_ind INTEGER,
|
||||
|
@ -205,9 +283,13 @@ def upgradeDatabase(self, db_version):
|
|||
|
||||
note VARCHAR,
|
||||
created_at BIGINT,
|
||||
PRIMARY KEY (record_id))'''))
|
||||
PRIMARY KEY (record_id))"""
|
||||
)
|
||||
)
|
||||
|
||||
session.execute(text('''
|
||||
session.execute(
|
||||
text(
|
||||
"""
|
||||
CREATE TABLE history (
|
||||
record_id INTEGER NOT NULL,
|
||||
concept_type INTEGER,
|
||||
|
@ -216,9 +298,13 @@ def upgradeDatabase(self, db_version):
|
|||
|
||||
note VARCHAR,
|
||||
created_at BIGINT,
|
||||
PRIMARY KEY (record_id))'''))
|
||||
PRIMARY KEY (record_id))"""
|
||||
)
|
||||
)
|
||||
|
||||
session.execute(text('''
|
||||
session.execute(
|
||||
text(
|
||||
"""
|
||||
CREATE TABLE bidstates (
|
||||
record_id INTEGER NOT NULL,
|
||||
active_ind INTEGER,
|
||||
|
@ -228,31 +314,53 @@ def upgradeDatabase(self, db_version):
|
|||
|
||||
note VARCHAR,
|
||||
created_at BIGINT,
|
||||
PRIMARY KEY (record_id))'''))
|
||||
PRIMARY KEY (record_id))"""
|
||||
)
|
||||
)
|
||||
|
||||
session.execute(text('ALTER TABLE wallets ADD COLUMN active_ind INTEGER'))
|
||||
session.execute(text('ALTER TABLE knownidentities ADD COLUMN active_ind INTEGER'))
|
||||
session.execute(text('ALTER TABLE eventqueue RENAME TO actions'))
|
||||
session.execute(text('ALTER TABLE actions RENAME COLUMN event_id TO action_id'))
|
||||
session.execute(text('ALTER TABLE actions RENAME COLUMN event_type TO action_type'))
|
||||
session.execute(text('ALTER TABLE actions RENAME COLUMN event_data TO action_data'))
|
||||
session.execute(text("ALTER TABLE wallets ADD COLUMN active_ind INTEGER"))
|
||||
session.execute(
|
||||
text("ALTER TABLE knownidentities ADD COLUMN active_ind INTEGER")
|
||||
)
|
||||
session.execute(text("ALTER TABLE eventqueue RENAME TO actions"))
|
||||
session.execute(
|
||||
text("ALTER TABLE actions RENAME COLUMN event_id TO action_id")
|
||||
)
|
||||
session.execute(
|
||||
text("ALTER TABLE actions RENAME COLUMN event_type TO action_type")
|
||||
)
|
||||
session.execute(
|
||||
text("ALTER TABLE actions RENAME COLUMN event_data TO action_data")
|
||||
)
|
||||
elif current_version == 14:
|
||||
db_version += 1
|
||||
session.execute(text('ALTER TABLE xmr_swaps ADD COLUMN coin_a_lock_release_msg_id BLOB'))
|
||||
session.execute(text('ALTER TABLE xmr_swaps RENAME COLUMN coin_a_lock_refund_spend_tx_msg_id TO coin_a_lock_spend_tx_msg_id'))
|
||||
session.execute(
|
||||
text("ALTER TABLE xmr_swaps ADD COLUMN coin_a_lock_release_msg_id BLOB")
|
||||
)
|
||||
session.execute(
|
||||
text(
|
||||
"ALTER TABLE xmr_swaps RENAME COLUMN coin_a_lock_refund_spend_tx_msg_id TO coin_a_lock_spend_tx_msg_id"
|
||||
)
|
||||
)
|
||||
elif current_version == 15:
|
||||
db_version += 1
|
||||
session.execute(text('''
|
||||
session.execute(
|
||||
text(
|
||||
"""
|
||||
CREATE TABLE notifications (
|
||||
record_id INTEGER NOT NULL,
|
||||
active_ind INTEGER,
|
||||
event_type INTEGER,
|
||||
event_data BLOB,
|
||||
created_at BIGINT,
|
||||
PRIMARY KEY (record_id))'''))
|
||||
PRIMARY KEY (record_id))"""
|
||||
)
|
||||
)
|
||||
elif current_version == 16:
|
||||
db_version += 1
|
||||
session.execute(text('''
|
||||
session.execute(
|
||||
text(
|
||||
"""
|
||||
CREATE TABLE prefunded_transactions (
|
||||
record_id INTEGER NOT NULL,
|
||||
active_ind INTEGER,
|
||||
|
@ -262,25 +370,43 @@ def upgradeDatabase(self, db_version):
|
|||
tx_type INTEGER,
|
||||
tx_data BLOB,
|
||||
used_by BLOB,
|
||||
PRIMARY KEY (record_id))'''))
|
||||
PRIMARY KEY (record_id))"""
|
||||
)
|
||||
)
|
||||
elif current_version == 17:
|
||||
db_version += 1
|
||||
session.execute(text('ALTER TABLE knownidentities ADD COLUMN automation_override INTEGER'))
|
||||
session.execute(text('ALTER TABLE knownidentities ADD COLUMN visibility_override INTEGER'))
|
||||
session.execute(text('ALTER TABLE knownidentities ADD COLUMN data BLOB'))
|
||||
session.execute(text('UPDATE knownidentities SET active_ind = 1'))
|
||||
session.execute(
|
||||
text(
|
||||
"ALTER TABLE knownidentities ADD COLUMN automation_override INTEGER"
|
||||
)
|
||||
)
|
||||
session.execute(
|
||||
text(
|
||||
"ALTER TABLE knownidentities ADD COLUMN visibility_override INTEGER"
|
||||
)
|
||||
)
|
||||
session.execute(text("ALTER TABLE knownidentities ADD COLUMN data BLOB"))
|
||||
session.execute(text("UPDATE knownidentities SET active_ind = 1"))
|
||||
elif current_version == 18:
|
||||
db_version += 1
|
||||
session.execute(text('ALTER TABLE xmr_split_data ADD COLUMN addr_from STRING'))
|
||||
session.execute(text('ALTER TABLE xmr_split_data ADD COLUMN addr_to STRING'))
|
||||
session.execute(
|
||||
text("ALTER TABLE xmr_split_data ADD COLUMN addr_from STRING")
|
||||
)
|
||||
session.execute(
|
||||
text("ALTER TABLE xmr_split_data ADD COLUMN addr_to STRING")
|
||||
)
|
||||
elif current_version == 19:
|
||||
db_version += 1
|
||||
session.execute(text('ALTER TABLE bidstates ADD COLUMN in_error INTEGER'))
|
||||
session.execute(text('ALTER TABLE bidstates ADD COLUMN swap_failed INTEGER'))
|
||||
session.execute(text('ALTER TABLE bidstates ADD COLUMN swap_ended INTEGER'))
|
||||
session.execute(text("ALTER TABLE bidstates ADD COLUMN in_error INTEGER"))
|
||||
session.execute(
|
||||
text("ALTER TABLE bidstates ADD COLUMN swap_failed INTEGER")
|
||||
)
|
||||
session.execute(text("ALTER TABLE bidstates ADD COLUMN swap_ended INTEGER"))
|
||||
elif current_version == 20:
|
||||
db_version += 1
|
||||
session.execute(text('''
|
||||
session.execute(
|
||||
text(
|
||||
"""
|
||||
CREATE TABLE message_links (
|
||||
record_id INTEGER NOT NULL,
|
||||
active_ind INTEGER,
|
||||
|
@ -292,18 +418,22 @@ def upgradeDatabase(self, db_version):
|
|||
msg_type INTEGER,
|
||||
msg_sequence INTEGER,
|
||||
msg_id BLOB,
|
||||
PRIMARY KEY (record_id))'''))
|
||||
session.execute(text('ALTER TABLE offers ADD COLUMN bid_reversed INTEGER'))
|
||||
PRIMARY KEY (record_id))"""
|
||||
)
|
||||
)
|
||||
session.execute(text("ALTER TABLE offers ADD COLUMN bid_reversed INTEGER"))
|
||||
elif current_version == 21:
|
||||
db_version += 1
|
||||
session.execute(text('ALTER TABLE offers ADD COLUMN proof_utxos BLOB'))
|
||||
session.execute(text('ALTER TABLE bids ADD COLUMN proof_utxos BLOB'))
|
||||
session.execute(text("ALTER TABLE offers ADD COLUMN proof_utxos BLOB"))
|
||||
session.execute(text("ALTER TABLE bids ADD COLUMN proof_utxos BLOB"))
|
||||
elif current_version == 22:
|
||||
db_version += 1
|
||||
session.execute(text('ALTER TABLE offers ADD COLUMN amount_to INTEGER'))
|
||||
session.execute(text("ALTER TABLE offers ADD COLUMN amount_to INTEGER"))
|
||||
elif current_version == 23:
|
||||
db_version += 1
|
||||
session.execute(text('''
|
||||
session.execute(
|
||||
text(
|
||||
"""
|
||||
CREATE TABLE checkedblocks (
|
||||
record_id INTEGER NOT NULL,
|
||||
created_at BIGINT,
|
||||
|
@ -311,17 +441,19 @@ def upgradeDatabase(self, db_version):
|
|||
block_height INTEGER,
|
||||
block_hash BLOB,
|
||||
block_time INTEGER,
|
||||
PRIMARY KEY (record_id))'''))
|
||||
session.execute(text('ALTER TABLE bids ADD COLUMN pkhash_buyer_to BLOB'))
|
||||
PRIMARY KEY (record_id))"""
|
||||
)
|
||||
)
|
||||
session.execute(text("ALTER TABLE bids ADD COLUMN pkhash_buyer_to BLOB"))
|
||||
if current_version != db_version:
|
||||
self.db_version = db_version
|
||||
self.setIntKV('db_version', db_version, session)
|
||||
self.setIntKV("db_version", db_version, session)
|
||||
session.commit()
|
||||
session.close()
|
||||
session.remove()
|
||||
self.log.info('Upgraded database to version {}'.format(self.db_version))
|
||||
self.log.info("Upgraded database to version {}".format(self.db_version))
|
||||
continue
|
||||
break
|
||||
|
||||
if db_version != CURRENT_DB_VERSION:
|
||||
raise ValueError('Unable to upgrade database.')
|
||||
raise ValueError("Unable to upgrade database.")
|
||||
|
|
|
@ -15,45 +15,142 @@ def remove_expired_data(self, time_offset: int = 0):
|
|||
try:
|
||||
session = self.openSession()
|
||||
|
||||
active_bids_insert = self.activeBidsQueryStr(now, '', 'b2')
|
||||
query_str = f'''
|
||||
active_bids_insert = self.activeBidsQueryStr(now, "", "b2")
|
||||
query_str = f"""
|
||||
SELECT o.offer_id FROM offers o
|
||||
WHERE o.expire_at <= :expired_at AND 0 = (SELECT COUNT(*) FROM bids b2 WHERE b2.offer_id = o.offer_id AND {active_bids_insert})
|
||||
'''
|
||||
"""
|
||||
num_offers = 0
|
||||
num_bids = 0
|
||||
offer_rows = session.execute(text(query_str), {'expired_at': now - time_offset})
|
||||
offer_rows = session.execute(text(query_str), {"expired_at": now - time_offset})
|
||||
for offer_row in offer_rows:
|
||||
num_offers += 1
|
||||
bid_rows = session.execute(text('SELECT bids.bid_id FROM bids WHERE bids.offer_id = :offer_id'), {'offer_id': offer_row[0]})
|
||||
bid_rows = session.execute(
|
||||
text("SELECT bids.bid_id FROM bids WHERE bids.offer_id = :offer_id"),
|
||||
{"offer_id": offer_row[0]},
|
||||
)
|
||||
for bid_row in bid_rows:
|
||||
num_bids += 1
|
||||
session.execute(text('DELETE FROM transactions WHERE transactions.bid_id = :bid_id'), {'bid_id': bid_row[0]})
|
||||
session.execute(text('DELETE FROM eventlog WHERE eventlog.linked_type = :type_ind AND eventlog.linked_id = :bid_id'), {'type_ind': int(Concepts.BID), 'bid_id': bid_row[0]})
|
||||
session.execute(text('DELETE FROM automationlinks WHERE automationlinks.linked_type = :type_ind AND automationlinks.linked_id = :bid_id'), {'type_ind': int(Concepts.BID), 'bid_id': bid_row[0]})
|
||||
session.execute(text('DELETE FROM prefunded_transactions WHERE prefunded_transactions.linked_type = :type_ind AND prefunded_transactions.linked_id = :bid_id'), {'type_ind': int(Concepts.BID), 'bid_id': bid_row[0]})
|
||||
session.execute(text('DELETE FROM history WHERE history.concept_type = :type_ind AND history.concept_id = :bid_id'), {'type_ind': int(Concepts.BID), 'bid_id': bid_row[0]})
|
||||
session.execute(text('DELETE FROM xmr_swaps WHERE xmr_swaps.bid_id = :bid_id'), {'bid_id': bid_row[0]})
|
||||
session.execute(text('DELETE FROM actions WHERE actions.linked_id = :bid_id'), {'bid_id': bid_row[0]})
|
||||
session.execute(text('DELETE FROM addresspool WHERE addresspool.bid_id = :bid_id'), {'bid_id': bid_row[0]})
|
||||
session.execute(text('DELETE FROM xmr_split_data WHERE xmr_split_data.bid_id = :bid_id'), {'bid_id': bid_row[0]})
|
||||
session.execute(text('DELETE FROM bids WHERE bids.bid_id = :bid_id'), {'bid_id': bid_row[0]})
|
||||
session.execute(text('DELETE FROM message_links WHERE linked_type = :type_ind AND linked_id = :linked_id'), {'type_ind': int(Concepts.BID), 'linked_id': bid_row[0]})
|
||||
session.execute(
|
||||
text(
|
||||
"DELETE FROM transactions WHERE transactions.bid_id = :bid_id"
|
||||
),
|
||||
{"bid_id": bid_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text(
|
||||
"DELETE FROM eventlog WHERE eventlog.linked_type = :type_ind AND eventlog.linked_id = :bid_id"
|
||||
),
|
||||
{"type_ind": int(Concepts.BID), "bid_id": bid_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text(
|
||||
"DELETE FROM automationlinks WHERE automationlinks.linked_type = :type_ind AND automationlinks.linked_id = :bid_id"
|
||||
),
|
||||
{"type_ind": int(Concepts.BID), "bid_id": bid_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text(
|
||||
"DELETE FROM prefunded_transactions WHERE prefunded_transactions.linked_type = :type_ind AND prefunded_transactions.linked_id = :bid_id"
|
||||
),
|
||||
{"type_ind": int(Concepts.BID), "bid_id": bid_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text(
|
||||
"DELETE FROM history WHERE history.concept_type = :type_ind AND history.concept_id = :bid_id"
|
||||
),
|
||||
{"type_ind": int(Concepts.BID), "bid_id": bid_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text("DELETE FROM xmr_swaps WHERE xmr_swaps.bid_id = :bid_id"),
|
||||
{"bid_id": bid_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text("DELETE FROM actions WHERE actions.linked_id = :bid_id"),
|
||||
{"bid_id": bid_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text("DELETE FROM addresspool WHERE addresspool.bid_id = :bid_id"),
|
||||
{"bid_id": bid_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text(
|
||||
"DELETE FROM xmr_split_data WHERE xmr_split_data.bid_id = :bid_id"
|
||||
),
|
||||
{"bid_id": bid_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text("DELETE FROM bids WHERE bids.bid_id = :bid_id"),
|
||||
{"bid_id": bid_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text(
|
||||
"DELETE FROM message_links WHERE linked_type = :type_ind AND linked_id = :linked_id"
|
||||
),
|
||||
{"type_ind": int(Concepts.BID), "linked_id": bid_row[0]},
|
||||
)
|
||||
|
||||
session.execute(text('DELETE FROM eventlog WHERE eventlog.linked_type = :type_ind AND eventlog.linked_id = :offer_id'), {'type_ind': int(Concepts.OFFER), 'offer_id': offer_row[0]})
|
||||
session.execute(text('DELETE FROM automationlinks WHERE automationlinks.linked_type = :type_ind AND automationlinks.linked_id = :offer_id'), {'type_ind': int(Concepts.OFFER), 'offer_id': offer_row[0]})
|
||||
session.execute(text('DELETE FROM prefunded_transactions WHERE prefunded_transactions.linked_type = :type_ind AND prefunded_transactions.linked_id = :offer_id'), {'type_ind': int(Concepts.OFFER), 'offer_id': offer_row[0]})
|
||||
session.execute(text('DELETE FROM history WHERE history.concept_type = :type_ind AND history.concept_id = :offer_id'), {'type_ind': int(Concepts.OFFER), 'offer_id': offer_row[0]})
|
||||
session.execute(text('DELETE FROM xmr_offers WHERE xmr_offers.offer_id = :offer_id'), {'offer_id': offer_row[0]})
|
||||
session.execute(text('DELETE FROM sentoffers WHERE sentoffers.offer_id = :offer_id'), {'offer_id': offer_row[0]})
|
||||
session.execute(text('DELETE FROM actions WHERE actions.linked_id = :offer_id'), {'offer_id': offer_row[0]})
|
||||
session.execute(text('DELETE FROM offers WHERE offers.offer_id = :offer_id'), {'offer_id': offer_row[0]})
|
||||
session.execute(text('DELETE FROM message_links WHERE linked_type = :type_ind AND linked_id = :offer_id'), {'type_ind': int(Concepts.OFFER), 'offer_id': offer_row[0]})
|
||||
session.execute(
|
||||
text(
|
||||
"DELETE FROM eventlog WHERE eventlog.linked_type = :type_ind AND eventlog.linked_id = :offer_id"
|
||||
),
|
||||
{"type_ind": int(Concepts.OFFER), "offer_id": offer_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text(
|
||||
"DELETE FROM automationlinks WHERE automationlinks.linked_type = :type_ind AND automationlinks.linked_id = :offer_id"
|
||||
),
|
||||
{"type_ind": int(Concepts.OFFER), "offer_id": offer_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text(
|
||||
"DELETE FROM prefunded_transactions WHERE prefunded_transactions.linked_type = :type_ind AND prefunded_transactions.linked_id = :offer_id"
|
||||
),
|
||||
{"type_ind": int(Concepts.OFFER), "offer_id": offer_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text(
|
||||
"DELETE FROM history WHERE history.concept_type = :type_ind AND history.concept_id = :offer_id"
|
||||
),
|
||||
{"type_ind": int(Concepts.OFFER), "offer_id": offer_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text("DELETE FROM xmr_offers WHERE xmr_offers.offer_id = :offer_id"),
|
||||
{"offer_id": offer_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text("DELETE FROM sentoffers WHERE sentoffers.offer_id = :offer_id"),
|
||||
{"offer_id": offer_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text("DELETE FROM actions WHERE actions.linked_id = :offer_id"),
|
||||
{"offer_id": offer_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text("DELETE FROM offers WHERE offers.offer_id = :offer_id"),
|
||||
{"offer_id": offer_row[0]},
|
||||
)
|
||||
session.execute(
|
||||
text(
|
||||
"DELETE FROM message_links WHERE linked_type = :type_ind AND linked_id = :offer_id"
|
||||
),
|
||||
{"type_ind": int(Concepts.OFFER), "offer_id": offer_row[0]},
|
||||
)
|
||||
|
||||
if num_offers > 0 or num_bids > 0:
|
||||
self.log.info('Removed data for {} expired offer{} and {} bid{}.'.format(num_offers, 's' if num_offers != 1 else '', num_bids, 's' if num_bids != 1 else ''))
|
||||
self.log.info(
|
||||
"Removed data for {} expired offer{} and {} bid{}.".format(
|
||||
num_offers,
|
||||
"s" if num_offers != 1 else "",
|
||||
num_bids,
|
||||
"s" if num_bids != 1 else "",
|
||||
)
|
||||
)
|
||||
|
||||
session.execute(text('DELETE FROM checkedblocks WHERE created_at <= :expired_at'), {'expired_at': now - time_offset})
|
||||
session.execute(
|
||||
text("DELETE FROM checkedblocks WHERE created_at <= :expired_at"),
|
||||
{"expired_at": now - time_offset},
|
||||
)
|
||||
|
||||
finally:
|
||||
self.closeSession(session)
|
||||
|
|
|
@ -13,8 +13,8 @@ def encodepoint(P):
|
|||
zi = edf.inv(P[2])
|
||||
x = (P[0] * zi) % edf.q
|
||||
y = (P[1] * zi) % edf.q
|
||||
y += ((x & 1) << 255)
|
||||
return y.to_bytes(32, byteorder='little')
|
||||
y += (x & 1) << 255
|
||||
return y.to_bytes(32, byteorder="little")
|
||||
|
||||
|
||||
def hashToEd25519(bytes_in):
|
||||
|
@ -22,8 +22,8 @@ def hashToEd25519(bytes_in):
|
|||
for i in range(1000):
|
||||
h255 = bytearray(hashed)
|
||||
x_sign = 0 if h255[31] & 0x80 == 0 else 1
|
||||
h255[31] &= 0x7f # Clear top bit
|
||||
y = int.from_bytes(h255, byteorder='little')
|
||||
h255[31] &= 0x7F # Clear top bit
|
||||
y = int.from_bytes(h255, byteorder="little")
|
||||
x = edf.xrecover(y, x_sign)
|
||||
if x == 0 and y == 1: # Skip infinity point
|
||||
continue
|
||||
|
@ -33,4 +33,4 @@ def hashToEd25519(bytes_in):
|
|||
if edf.isoncurve(P) and edf.is_identity(edf.scalarmult(P, edf.l)):
|
||||
return P
|
||||
hashed = hashlib.sha256(hashed).digest()
|
||||
raise ValueError('hashToEd25519 failed')
|
||||
raise ValueError("hashToEd25519 failed")
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import json
|
||||
|
||||
|
||||
class Explorer():
|
||||
class Explorer:
|
||||
def __init__(self, swapclient, coin_type, base_url):
|
||||
self.swapclient = swapclient
|
||||
self.coin_type = coin_type
|
||||
|
@ -15,82 +15,94 @@ class Explorer():
|
|||
self.log = self.swapclient.log
|
||||
|
||||
def readURL(self, url):
|
||||
self.log.debug('Explorer url: {}'.format(url))
|
||||
self.log.debug("Explorer url: {}".format(url))
|
||||
return self.swapclient.readURL(url)
|
||||
|
||||
|
||||
class ExplorerInsight(Explorer):
|
||||
def getChainHeight(self):
|
||||
return json.loads(self.readURL(self.base_url + '/sync'))['blockChainHeight']
|
||||
return json.loads(self.readURL(self.base_url + "/sync"))["blockChainHeight"]
|
||||
|
||||
def getBlock(self, block_hash):
|
||||
data = json.loads(self.readURL(self.base_url + '/block/{}'.format(block_hash)))
|
||||
data = json.loads(self.readURL(self.base_url + "/block/{}".format(block_hash)))
|
||||
return data
|
||||
|
||||
def getTransaction(self, txid):
|
||||
data = json.loads(self.readURL(self.base_url + '/tx/{}'.format(txid)))
|
||||
data = json.loads(self.readURL(self.base_url + "/tx/{}".format(txid)))
|
||||
return data
|
||||
|
||||
def getBalance(self, address):
|
||||
data = json.loads(self.readURL(self.base_url + '/addr/{}/balance'.format(address)))
|
||||
data = json.loads(
|
||||
self.readURL(self.base_url + "/addr/{}/balance".format(address))
|
||||
)
|
||||
return data
|
||||
|
||||
def lookupUnspentByAddress(self, address):
|
||||
data = json.loads(self.readURL(self.base_url + '/addr/{}/utxo'.format(address)))
|
||||
data = json.loads(self.readURL(self.base_url + "/addr/{}/utxo".format(address)))
|
||||
rv = []
|
||||
for utxo in data:
|
||||
rv.append({
|
||||
'txid': utxo['txid'],
|
||||
'index': utxo['vout'],
|
||||
'height': utxo['height'],
|
||||
'n_conf': utxo['confirmations'],
|
||||
'value': utxo['satoshis'],
|
||||
})
|
||||
rv.append(
|
||||
{
|
||||
"txid": utxo["txid"],
|
||||
"index": utxo["vout"],
|
||||
"height": utxo["height"],
|
||||
"n_conf": utxo["confirmations"],
|
||||
"value": utxo["satoshis"],
|
||||
}
|
||||
)
|
||||
return rv
|
||||
|
||||
|
||||
class ExplorerBitAps(Explorer):
|
||||
def getChainHeight(self):
|
||||
return json.loads(self.readURL(self.base_url + '/block/last'))['data']['block']['height']
|
||||
return json.loads(self.readURL(self.base_url + "/block/last"))["data"]["block"][
|
||||
"height"
|
||||
]
|
||||
|
||||
def getBlock(self, block_hash):
|
||||
data = json.loads(self.readURL(self.base_url + '/block/{}'.format(block_hash)))
|
||||
data = json.loads(self.readURL(self.base_url + "/block/{}".format(block_hash)))
|
||||
return data
|
||||
|
||||
def getTransaction(self, txid):
|
||||
data = json.loads(self.readURL(self.base_url + '/transaction/{}'.format(txid)))
|
||||
data = json.loads(self.readURL(self.base_url + "/transaction/{}".format(txid)))
|
||||
return data
|
||||
|
||||
def getBalance(self, address):
|
||||
data = json.loads(self.readURL(self.base_url + '/address/state/' + address))
|
||||
return data['data']['balance']
|
||||
data = json.loads(self.readURL(self.base_url + "/address/state/" + address))
|
||||
return data["data"]["balance"]
|
||||
|
||||
def lookupUnspentByAddress(self, address):
|
||||
# Can't get unspents return only if exactly one transaction exists
|
||||
data = json.loads(self.readURL(self.base_url + '/address/transactions/' + address))
|
||||
data = json.loads(
|
||||
self.readURL(self.base_url + "/address/transactions/" + address)
|
||||
)
|
||||
try:
|
||||
assert data['data']['list'] == 1
|
||||
assert data["data"]["list"] == 1
|
||||
except Exception as ex:
|
||||
self.log.debug('Explorer error: {}'.format(str(ex)))
|
||||
self.log.debug("Explorer error: {}".format(str(ex)))
|
||||
return None
|
||||
tx = data['data']['list'][0]
|
||||
tx_data = json.loads(self.readURL(self.base_url + '/transaction/{}'.format(tx['txId'])))['data']
|
||||
tx = data["data"]["list"][0]
|
||||
tx_data = json.loads(
|
||||
self.readURL(self.base_url + "/transaction/{}".format(tx["txId"]))
|
||||
)["data"]
|
||||
|
||||
for i, vout in tx_data['vOut'].items():
|
||||
if vout['address'] == address:
|
||||
return [{
|
||||
'txid': tx_data['txId'],
|
||||
'index': int(i),
|
||||
'height': tx_data['blockHeight'],
|
||||
'n_conf': tx_data['confirmations'],
|
||||
'value': vout['value'],
|
||||
}]
|
||||
for i, vout in tx_data["vOut"].items():
|
||||
if vout["address"] == address:
|
||||
return [
|
||||
{
|
||||
"txid": tx_data["txId"],
|
||||
"index": int(i),
|
||||
"height": tx_data["blockHeight"],
|
||||
"n_conf": tx_data["confirmations"],
|
||||
"value": vout["value"],
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
class ExplorerChainz(Explorer):
|
||||
def getChainHeight(self):
|
||||
return int(self.readURL(self.base_url + '?q=getblockcount'))
|
||||
return int(self.readURL(self.base_url + "?q=getblockcount"))
|
||||
|
||||
def lookupUnspentByAddress(self, address):
|
||||
chain_height = self.getChainHeight()
|
||||
self.log.debug('[rm] chain_height %d', chain_height)
|
||||
self.log.debug("[rm] chain_height %d", chain_height)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2019-2024 tecnovert
|
||||
# Copyright (c) 2024 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -56,12 +57,12 @@ from .ui.page_identity import page_identity
|
|||
from .ui.page_smsgaddresses import page_smsgaddresses
|
||||
from .ui.page_debug import page_debug
|
||||
|
||||
env = Environment(loader=PackageLoader('basicswap', 'templates'))
|
||||
env.filters['formatts'] = format_timestamp
|
||||
env = Environment(loader=PackageLoader("basicswap", "templates"))
|
||||
env.filters["formatts"] = format_timestamp
|
||||
|
||||
|
||||
def extractDomain(url):
|
||||
return url.split('://', 1)[1].split('/', 1)[0]
|
||||
return url.split("://", 1)[1].split("/", 1)[0]
|
||||
|
||||
|
||||
def listAvailableExplorers(swap_client):
|
||||
|
@ -69,40 +70,47 @@ def listAvailableExplorers(swap_client):
|
|||
for c in Coins:
|
||||
if c not in chainparams:
|
||||
continue
|
||||
for i, e in enumerate(swap_client.coin_clients[c]['explorers']):
|
||||
explorers.append(('{}_{}'.format(int(c), i), getCoinName(c) + ' - ' + extractDomain(e.base_url)))
|
||||
for i, e in enumerate(swap_client.coin_clients[c]["explorers"]):
|
||||
explorers.append(
|
||||
(
|
||||
"{}_{}".format(int(c), i),
|
||||
getCoinName(c) + " - " + extractDomain(e.base_url),
|
||||
)
|
||||
)
|
||||
return explorers
|
||||
|
||||
|
||||
def listExplorerActions(swap_client):
|
||||
actions = [('height', 'Chain Height'),
|
||||
('block', 'Get Block'),
|
||||
('tx', 'Get Transaction'),
|
||||
('balance', 'Address Balance'),
|
||||
('unspent', 'List Unspent')]
|
||||
actions = [
|
||||
("height", "Chain Height"),
|
||||
("block", "Get Block"),
|
||||
("tx", "Get Transaction"),
|
||||
("balance", "Address Balance"),
|
||||
("unspent", "List Unspent"),
|
||||
]
|
||||
return actions
|
||||
|
||||
|
||||
def parse_cmd(cmd: str, type_map: str):
|
||||
params = shlex.split(cmd)
|
||||
if len(params) < 1:
|
||||
return '', []
|
||||
return "", []
|
||||
method = params[0]
|
||||
typed_params = []
|
||||
params = params[1:]
|
||||
|
||||
for i, param in enumerate(params):
|
||||
if i >= len(type_map):
|
||||
type_ind = 's'
|
||||
type_ind = "s"
|
||||
else:
|
||||
type_ind = type_map[i]
|
||||
if type_ind == 'i':
|
||||
if type_ind == "i":
|
||||
typed_params.append(int(param))
|
||||
elif type_ind == 'f':
|
||||
elif type_ind == "f":
|
||||
typed_params.append(float(param))
|
||||
elif type_ind == 'b':
|
||||
elif type_ind == "b":
|
||||
typed_params.append(toBool(param))
|
||||
elif type_ind == 'j':
|
||||
elif type_ind == "j":
|
||||
typed_params.append(json.loads(param))
|
||||
else:
|
||||
typed_params.append(param)
|
||||
|
@ -122,99 +130,112 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
return os.urandom(8).hex()
|
||||
|
||||
def checkForm(self, post_string, name, messages):
|
||||
if post_string == '':
|
||||
if post_string == "":
|
||||
return None
|
||||
form_data = parse.parse_qs(post_string)
|
||||
form_id = form_data[b'formid'][0].decode('utf-8')
|
||||
form_id = form_data[b"formid"][0].decode("utf-8")
|
||||
if self.server.last_form_id.get(name, None) == form_id:
|
||||
messages.append('Prevented double submit for form {}.'.format(form_id))
|
||||
messages.append("Prevented double submit for form {}.".format(form_id))
|
||||
return None
|
||||
self.server.last_form_id[name] = form_id
|
||||
return form_data
|
||||
|
||||
def render_template(self, template, args_dict, status_code=200, version=__version__):
|
||||
def render_template(
|
||||
self, template, args_dict, status_code=200, version=__version__
|
||||
):
|
||||
swap_client = self.server.swap_client
|
||||
if swap_client.ws_server:
|
||||
args_dict['ws_port'] = swap_client.ws_server.client_port
|
||||
args_dict["ws_port"] = swap_client.ws_server.client_port
|
||||
if swap_client.debug:
|
||||
args_dict['debug_mode'] = True
|
||||
args_dict["debug_mode"] = True
|
||||
if swap_client.debug_ui:
|
||||
args_dict['debug_ui_mode'] = True
|
||||
args_dict["debug_ui_mode"] = True
|
||||
if swap_client.use_tor_proxy:
|
||||
args_dict['use_tor_proxy'] = True
|
||||
args_dict["use_tor_proxy"] = True
|
||||
# TODO: Cache value?
|
||||
try:
|
||||
tor_state = get_tor_established_state(swap_client)
|
||||
args_dict['tor_established'] = True if tor_state == '1' else False
|
||||
args_dict["tor_established"] = True if tor_state == "1" else False
|
||||
except Exception as e:
|
||||
args_dict['tor_established'] = False
|
||||
args_dict["tor_established"] = False
|
||||
if swap_client.debug:
|
||||
swap_client.log.error(f"Error getting Tor state: {str(e)}")
|
||||
swap_client.log.error(traceback.format_exc())
|
||||
|
||||
if swap_client._show_notifications:
|
||||
args_dict['notifications'] = swap_client.getNotifications()
|
||||
args_dict["notifications"] = swap_client.getNotifications()
|
||||
|
||||
if 'messages' in args_dict:
|
||||
if "messages" in args_dict:
|
||||
messages_with_ids = []
|
||||
for msg in args_dict['messages']:
|
||||
for msg in args_dict["messages"]:
|
||||
messages_with_ids.append((self.server.msg_id_counter, msg))
|
||||
self.server.msg_id_counter += 1
|
||||
args_dict['messages'] = messages_with_ids
|
||||
if 'err_messages' in args_dict:
|
||||
args_dict["messages"] = messages_with_ids
|
||||
if "err_messages" in args_dict:
|
||||
err_messages_with_ids = []
|
||||
for msg in args_dict['err_messages']:
|
||||
for msg in args_dict["err_messages"]:
|
||||
err_messages_with_ids.append((self.server.msg_id_counter, msg))
|
||||
self.server.msg_id_counter += 1
|
||||
args_dict['err_messages'] = err_messages_with_ids
|
||||
args_dict["err_messages"] = err_messages_with_ids
|
||||
|
||||
shutdown_token = os.urandom(8).hex()
|
||||
self.server.session_tokens['shutdown'] = shutdown_token
|
||||
args_dict['shutdown_token'] = shutdown_token
|
||||
self.server.session_tokens["shutdown"] = shutdown_token
|
||||
args_dict["shutdown_token"] = shutdown_token
|
||||
|
||||
encrypted, locked = swap_client.getLockedState()
|
||||
args_dict['encrypted'] = encrypted
|
||||
args_dict['locked'] = locked
|
||||
args_dict["encrypted"] = encrypted
|
||||
args_dict["locked"] = locked
|
||||
|
||||
if self.server.msg_id_counter >= 0x7FFFFFFF:
|
||||
self.server.msg_id_counter = 0
|
||||
|
||||
args_dict['version'] = version
|
||||
args_dict["version"] = version
|
||||
|
||||
self.putHeaders(status_code, 'text/html')
|
||||
return bytes(template.render(
|
||||
title=self.server.title,
|
||||
h2=self.server.title,
|
||||
form_id=self.generate_form_id(),
|
||||
**args_dict,
|
||||
), 'UTF-8')
|
||||
self.putHeaders(status_code, "text/html")
|
||||
return bytes(
|
||||
template.render(
|
||||
title=self.server.title,
|
||||
h2=self.server.title,
|
||||
form_id=self.generate_form_id(),
|
||||
**args_dict,
|
||||
),
|
||||
"UTF-8",
|
||||
)
|
||||
|
||||
def render_simple_template(self, template, args_dict):
|
||||
swap_client = self.server.swap_client
|
||||
return bytes(template.render(
|
||||
title=self.server.title,
|
||||
**args_dict,
|
||||
), 'UTF-8')
|
||||
return bytes(
|
||||
template.render(
|
||||
title=self.server.title,
|
||||
**args_dict,
|
||||
),
|
||||
"UTF-8",
|
||||
)
|
||||
|
||||
def page_info(self, info_str, post_string=None):
|
||||
template = env.get_template('info.html')
|
||||
template = env.get_template("info.html")
|
||||
swap_client = self.server.swap_client
|
||||
summary = swap_client.getSummary()
|
||||
return self.render_template(template, {
|
||||
'title_str': 'BasicSwap Info',
|
||||
'message_str': info_str,
|
||||
'summary': summary,
|
||||
})
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"title_str": "BasicSwap Info",
|
||||
"message_str": info_str,
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
||||
def page_error(self, error_str, post_string=None):
|
||||
template = env.get_template('error.html')
|
||||
template = env.get_template("error.html")
|
||||
swap_client = self.server.swap_client
|
||||
summary = swap_client.getSummary()
|
||||
return self.render_template(template, {
|
||||
'title_str': 'BasicSwap Error',
|
||||
'message_str': error_str,
|
||||
'summary': summary,
|
||||
})
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"title_str": "BasicSwap Error",
|
||||
"message_str": error_str,
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
||||
def page_explorers(self, url_split, post_string):
|
||||
swap_client = self.server.swap_client
|
||||
|
@ -226,42 +247,49 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
action = -1
|
||||
messages = []
|
||||
err_messages = []
|
||||
form_data = self.checkForm(post_string, 'explorers', err_messages)
|
||||
form_data = self.checkForm(post_string, "explorers", err_messages)
|
||||
if form_data:
|
||||
|
||||
explorer = form_data[b'explorer'][0].decode('utf-8')
|
||||
action = form_data[b'action'][0].decode('utf-8')
|
||||
explorer = form_data[b"explorer"][0].decode("utf-8")
|
||||
action = form_data[b"action"][0].decode("utf-8")
|
||||
|
||||
args = '' if b'args' not in form_data else form_data[b'args'][0].decode('utf-8')
|
||||
args = (
|
||||
""
|
||||
if b"args" not in form_data
|
||||
else form_data[b"args"][0].decode("utf-8")
|
||||
)
|
||||
try:
|
||||
c, e = explorer.split('_')
|
||||
exp = swap_client.coin_clients[Coins(int(c))]['explorers'][int(e)]
|
||||
if action == 'height':
|
||||
c, e = explorer.split("_")
|
||||
exp = swap_client.coin_clients[Coins(int(c))]["explorers"][int(e)]
|
||||
if action == "height":
|
||||
result = str(exp.getChainHeight())
|
||||
elif action == 'block':
|
||||
elif action == "block":
|
||||
result = dumpj(exp.getBlock(args))
|
||||
elif action == 'tx':
|
||||
elif action == "tx":
|
||||
result = dumpj(exp.getTransaction(args))
|
||||
elif action == 'balance':
|
||||
elif action == "balance":
|
||||
result = dumpj(exp.getBalance(args))
|
||||
elif action == 'unspent':
|
||||
elif action == "unspent":
|
||||
result = dumpj(exp.lookupUnspentByAddress(args))
|
||||
else:
|
||||
result = 'Unknown action'
|
||||
result = "Unknown action"
|
||||
except Exception as ex:
|
||||
result = str(ex)
|
||||
|
||||
template = env.get_template('explorers.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'explorers': listAvailableExplorers(swap_client),
|
||||
'explorer': explorer,
|
||||
'actions': listExplorerActions(swap_client),
|
||||
'action': action,
|
||||
'result': result,
|
||||
'summary': summary,
|
||||
})
|
||||
template = env.get_template("explorers.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"messages": messages,
|
||||
"err_messages": err_messages,
|
||||
"explorers": listAvailableExplorers(swap_client),
|
||||
"explorer": explorer,
|
||||
"actions": listExplorerActions(swap_client),
|
||||
"action": action,
|
||||
"result": result,
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
||||
def page_rpc(self, url_split, post_string):
|
||||
swap_client = self.server.swap_client
|
||||
|
@ -269,34 +297,33 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
summary = swap_client.getSummary()
|
||||
|
||||
result = None
|
||||
cmd = ''
|
||||
cmd = ""
|
||||
coin_type_selected = -1
|
||||
coin_type = -1
|
||||
coin_id = -1
|
||||
call_type = 'cli'
|
||||
type_map = ''
|
||||
call_type = "cli"
|
||||
type_map = ""
|
||||
messages = []
|
||||
err_messages = []
|
||||
form_data = self.checkForm(post_string, 'rpc', err_messages)
|
||||
form_data = self.checkForm(post_string, "rpc", err_messages)
|
||||
if form_data:
|
||||
try:
|
||||
call_type = get_data_entry_or(form_data, 'call_type', 'cli')
|
||||
type_map = get_data_entry_or(form_data, 'type_map', '')
|
||||
call_type = get_data_entry_or(form_data, "call_type", "cli")
|
||||
type_map = get_data_entry_or(form_data, "type_map", "")
|
||||
try:
|
||||
coin_type_selected = get_data_entry(form_data, 'coin_type')
|
||||
coin_type_split = coin_type_selected.split(',')
|
||||
coin_type_selected = get_data_entry(form_data, "coin_type")
|
||||
coin_type_split = coin_type_selected.split(",")
|
||||
coin_type = Coins(int(coin_type_split[0]))
|
||||
coin_variant = int(coin_type_split[1])
|
||||
except Exception:
|
||||
raise ValueError('Unknown Coin Type')
|
||||
raise ValueError("Unknown Coin Type")
|
||||
|
||||
if coin_type in (Coins.DCR,):
|
||||
call_type = 'http'
|
||||
call_type = "http"
|
||||
|
||||
try:
|
||||
cmd = get_data_entry(form_data, 'cmd')
|
||||
cmd = get_data_entry(form_data, "cmd")
|
||||
except Exception:
|
||||
raise ValueError('Invalid command')
|
||||
raise ValueError("Invalid command")
|
||||
if coin_type in (Coins.XMR, Coins.WOW):
|
||||
ci = swap_client.ci(coin_type)
|
||||
arr = cmd.split(None, 1)
|
||||
|
@ -311,10 +338,10 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
params = None
|
||||
rv = ci.rpc2(method, params)
|
||||
else:
|
||||
raise ValueError('Unknown RPC variant')
|
||||
raise ValueError("Unknown RPC variant")
|
||||
result = json.dumps(rv, indent=4)
|
||||
else:
|
||||
if call_type == 'http':
|
||||
if call_type == "http":
|
||||
ci = swap_client.ci(coin_type)
|
||||
method, params = parse_cmd(cmd, type_map)
|
||||
if coin_variant == 1:
|
||||
|
@ -322,50 +349,56 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
elif coin_variant == 2:
|
||||
rv = ci.rpc_wallet_mweb(method, params)
|
||||
else:
|
||||
if coin_type in (Coins.DCR, ):
|
||||
if coin_type in (Coins.DCR,):
|
||||
rv = ci.rpc(method, params)
|
||||
else:
|
||||
rv = ci.rpc_wallet(method, params)
|
||||
if not isinstance(rv, str):
|
||||
rv = json.dumps(rv, indent=4)
|
||||
result = cmd + '\n' + rv
|
||||
result = cmd + "\n" + rv
|
||||
else:
|
||||
result = cmd + '\n' + swap_client.callcoincli(coin_type, cmd)
|
||||
result = cmd + "\n" + swap_client.callcoincli(coin_type, cmd)
|
||||
except Exception as ex:
|
||||
result = cmd + '\n' + str(ex)
|
||||
result = cmd + "\n" + str(ex)
|
||||
if self.server.swap_client.debug is True:
|
||||
self.server.swap_client.log.error(traceback.format_exc())
|
||||
|
||||
template = env.get_template('rpc.html')
|
||||
template = env.get_template("rpc.html")
|
||||
|
||||
coin_available = listAvailableCoins(swap_client, with_variants=False)
|
||||
with_xmr: bool = any(c[0] == Coins.XMR for c in coin_available)
|
||||
with_wow: bool = any(c[0] == Coins.WOW for c in coin_available)
|
||||
coins = [(str(c[0]) + ',0', c[1]) for c in coin_available if c[0] not in (Coins.XMR, Coins.WOW)]
|
||||
coins = [
|
||||
(str(c[0]) + ",0", c[1])
|
||||
for c in coin_available
|
||||
if c[0] not in (Coins.XMR, Coins.WOW)
|
||||
]
|
||||
|
||||
if any(c[0] == Coins.DCR for c in coin_available):
|
||||
coins.append((str(int(Coins.DCR)) + ',1', 'Decred Wallet'))
|
||||
coins.append((str(int(Coins.DCR)) + ",1", "Decred Wallet"))
|
||||
if any(c[0] == Coins.LTC for c in coin_available):
|
||||
coins.append((str(int(Coins.LTC)) + ',2', 'Litecoin MWEB Wallet'))
|
||||
coins.append((str(int(Coins.LTC)) + ",2", "Litecoin MWEB Wallet"))
|
||||
if with_xmr:
|
||||
coins.append((str(int(Coins.XMR)) + ',0', 'Monero'))
|
||||
coins.append((str(int(Coins.XMR)) + ',1', 'Monero JSON'))
|
||||
coins.append((str(int(Coins.XMR)) + ',2', 'Monero Wallet'))
|
||||
coins.append((str(int(Coins.XMR)) + ",0", "Monero"))
|
||||
coins.append((str(int(Coins.XMR)) + ",1", "Monero JSON"))
|
||||
coins.append((str(int(Coins.XMR)) + ",2", "Monero Wallet"))
|
||||
if with_wow:
|
||||
coins.append((str(int(Coins.WOW)) + ',0', 'Wownero'))
|
||||
coins.append((str(int(Coins.WOW)) + ',1', 'Wownero JSON'))
|
||||
coins.append((str(int(Coins.WOW)) + ',2', 'Wownero Wallet'))
|
||||
coins.append((str(int(Coins.WOW)) + ",0", "Wownero"))
|
||||
coins.append((str(int(Coins.WOW)) + ",1", "Wownero JSON"))
|
||||
coins.append((str(int(Coins.WOW)) + ",2", "Wownero Wallet"))
|
||||
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'coins': coins,
|
||||
'coin_type': coin_type_selected,
|
||||
'call_type': call_type,
|
||||
'result': result,
|
||||
'messages': messages,
|
||||
'summary': summary,
|
||||
})
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"messages": messages,
|
||||
"err_messages": err_messages,
|
||||
"coins": coins,
|
||||
"coin_type": coin_type_selected,
|
||||
"call_type": call_type,
|
||||
"result": result,
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
||||
def page_active(self, url_split, post_string):
|
||||
swap_client = self.server.swap_client
|
||||
|
@ -373,12 +406,24 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
active_swaps = swap_client.listSwapsInProgress()
|
||||
summary = swap_client.getSummary()
|
||||
|
||||
template = env.get_template('active.html')
|
||||
return self.render_template(template, {
|
||||
'refresh': 30,
|
||||
'active_swaps': [(s[0].hex(), s[1], strBidState(s[2]), strTxState(s[3]), strTxState(s[4])) for s in active_swaps],
|
||||
'summary': summary,
|
||||
})
|
||||
template = env.get_template("active.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"refresh": 30,
|
||||
"active_swaps": [
|
||||
(
|
||||
s[0].hex(),
|
||||
s[1],
|
||||
strBidState(s[2]),
|
||||
strTxState(s[3]),
|
||||
strTxState(s[4]),
|
||||
)
|
||||
for s in active_swaps
|
||||
],
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
||||
def page_watched(self, url_split, post_string):
|
||||
swap_client = self.server.swap_client
|
||||
|
@ -386,60 +431,68 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
watched_outputs, last_scanned = swap_client.listWatchedOutputs()
|
||||
summary = swap_client.getSummary()
|
||||
|
||||
template = env.get_template('watched.html')
|
||||
return self.render_template(template, {
|
||||
'refresh': 30,
|
||||
'last_scanned': [(getCoinName(ls[0]), ls[1]) for ls in last_scanned],
|
||||
'watched_outputs': [(wo[1].hex(), getCoinName(wo[0]), wo[2], wo[3], int(wo[4])) for wo in watched_outputs],
|
||||
'summary': summary,
|
||||
})
|
||||
template = env.get_template("watched.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"refresh": 30,
|
||||
"last_scanned": [(getCoinName(ls[0]), ls[1]) for ls in last_scanned],
|
||||
"watched_outputs": [
|
||||
(wo[1].hex(), getCoinName(wo[0]), wo[2], wo[3], int(wo[4]))
|
||||
for wo in watched_outputs
|
||||
],
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
||||
def page_shutdown(self, url_split, post_string):
|
||||
swap_client = self.server.swap_client
|
||||
|
||||
if len(url_split) > 2:
|
||||
token = url_split[2]
|
||||
expect_token = self.server.session_tokens.get('shutdown', None)
|
||||
expect_token = self.server.session_tokens.get("shutdown", None)
|
||||
if token != expect_token:
|
||||
return self.page_info('Unexpected token, still running.')
|
||||
return self.page_info("Unexpected token, still running.")
|
||||
|
||||
swap_client.stopRunning()
|
||||
|
||||
return self.page_info('Shutting down')
|
||||
return self.page_info("Shutting down")
|
||||
|
||||
def page_index(self, url_split):
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
summary = swap_client.getSummary()
|
||||
self.send_response(302)
|
||||
self.send_header('Location', '/offers')
|
||||
self.send_header("Location", "/offers")
|
||||
self.end_headers()
|
||||
return b''
|
||||
return b""
|
||||
|
||||
def page_404(self, url_split):
|
||||
swap_client = self.server.swap_client
|
||||
summary = swap_client.getSummary()
|
||||
template = env.get_template('404.html')
|
||||
return self.render_template(template, {
|
||||
'summary': summary,
|
||||
})
|
||||
template = env.get_template("404.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
||||
def putHeaders(self, status_code, content_type):
|
||||
self.send_response(status_code)
|
||||
if self.server.allow_cors:
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.send_header('Content-Type', content_type)
|
||||
self.send_header("Access-Control-Allow-Origin", "*")
|
||||
self.send_header("Content-Type", content_type)
|
||||
self.end_headers()
|
||||
|
||||
def handle_http(self, status_code, path, post_string='', is_json=False):
|
||||
def handle_http(self, status_code, path, post_string="", is_json=False):
|
||||
swap_client = self.server.swap_client
|
||||
parsed = parse.urlparse(self.path)
|
||||
url_split = parsed.path.split('/')
|
||||
if post_string == '' and len(parsed.query) > 0:
|
||||
url_split = parsed.path.split("/")
|
||||
if post_string == "" and len(parsed.query) > 0:
|
||||
post_string = parsed.query
|
||||
if len(url_split) > 1 and url_split[1] == 'json':
|
||||
if len(url_split) > 1 and url_split[1] == "json":
|
||||
try:
|
||||
self.putHeaders(status_code, 'text/plain')
|
||||
self.putHeaders(status_code, "text/plain")
|
||||
func = js_url_to_function(url_split)
|
||||
return func(self, url_split, post_string, is_json)
|
||||
except Exception as ex:
|
||||
|
@ -447,37 +500,42 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
swap_client.log.error(traceback.format_exc())
|
||||
return js_error(self, str(ex))
|
||||
|
||||
if len(url_split) > 1 and url_split[1] == 'static':
|
||||
if len(url_split) > 1 and url_split[1] == "static":
|
||||
try:
|
||||
static_path = os.path.join(os.path.dirname(__file__), 'static')
|
||||
if len(url_split) > 3 and url_split[2] == 'sequence_diagrams':
|
||||
with open(os.path.join(static_path, 'sequence_diagrams', url_split[3]), 'rb') as fp:
|
||||
self.putHeaders(status_code, 'image/svg+xml')
|
||||
static_path = os.path.join(os.path.dirname(__file__), "static")
|
||||
if len(url_split) > 3 and url_split[2] == "sequence_diagrams":
|
||||
with open(
|
||||
os.path.join(static_path, "sequence_diagrams", url_split[3]),
|
||||
"rb",
|
||||
) as fp:
|
||||
self.putHeaders(status_code, "image/svg+xml")
|
||||
return fp.read()
|
||||
elif len(url_split) > 3 and url_split[2] == 'images':
|
||||
elif len(url_split) > 3 and url_split[2] == "images":
|
||||
filename = os.path.join(*url_split[3:])
|
||||
_, extension = os.path.splitext(filename)
|
||||
mime_type = {
|
||||
'.svg': 'image/svg+xml',
|
||||
'.png': 'image/png',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.gif': 'image/gif',
|
||||
'.ico': 'image/x-icon',
|
||||
}.get(extension, '')
|
||||
if mime_type == '':
|
||||
raise ValueError('Unknown file type ' + filename)
|
||||
with open(os.path.join(static_path, 'images', filename), 'rb') as fp:
|
||||
".svg": "image/svg+xml",
|
||||
".png": "image/png",
|
||||
".jpg": "image/jpeg",
|
||||
".gif": "image/gif",
|
||||
".ico": "image/x-icon",
|
||||
}.get(extension, "")
|
||||
if mime_type == "":
|
||||
raise ValueError("Unknown file type " + filename)
|
||||
with open(
|
||||
os.path.join(static_path, "images", filename), "rb"
|
||||
) as fp:
|
||||
self.putHeaders(status_code, mime_type)
|
||||
return fp.read()
|
||||
elif len(url_split) > 3 and url_split[2] == 'css':
|
||||
elif len(url_split) > 3 and url_split[2] == "css":
|
||||
filename = os.path.join(*url_split[3:])
|
||||
with open(os.path.join(static_path, 'css', filename), 'rb') as fp:
|
||||
self.putHeaders(status_code, 'text/css; charset=utf-8')
|
||||
with open(os.path.join(static_path, "css", filename), "rb") as fp:
|
||||
self.putHeaders(status_code, "text/css; charset=utf-8")
|
||||
return fp.read()
|
||||
elif len(url_split) > 3 and url_split[2] == 'js':
|
||||
elif len(url_split) > 3 and url_split[2] == "js":
|
||||
filename = os.path.join(*url_split[3:])
|
||||
with open(os.path.join(static_path, 'js', filename), 'rb') as fp:
|
||||
self.putHeaders(status_code, 'application/javascript')
|
||||
with open(os.path.join(static_path, "js", filename), "rb") as fp:
|
||||
self.putHeaders(status_code, "application/javascript")
|
||||
return fp.read()
|
||||
else:
|
||||
return self.page_404(url_split)
|
||||
|
@ -492,63 +550,63 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
if len(url_split) > 1:
|
||||
page = url_split[1]
|
||||
|
||||
if page == 'active':
|
||||
if page == "active":
|
||||
return self.page_active(url_split, post_string)
|
||||
if page == 'wallets':
|
||||
if page == "wallets":
|
||||
return page_wallets(self, url_split, post_string)
|
||||
if page == 'wallet':
|
||||
if page == "wallet":
|
||||
return page_wallet(self, url_split, post_string)
|
||||
if page == 'settings':
|
||||
if page == "settings":
|
||||
return page_settings(self, url_split, post_string)
|
||||
if page == 'error':
|
||||
if page == "error":
|
||||
return self.page_error(url_split, post_string)
|
||||
if page == 'info':
|
||||
if page == "info":
|
||||
return self.page_info(url_split, post_string)
|
||||
if page == 'rpc':
|
||||
if page == "rpc":
|
||||
return self.page_rpc(url_split, post_string)
|
||||
if page == 'debug':
|
||||
if page == "debug":
|
||||
return page_debug(self, url_split, post_string)
|
||||
if page == 'explorers':
|
||||
if page == "explorers":
|
||||
return self.page_explorers(url_split, post_string)
|
||||
if page == 'offer':
|
||||
if page == "offer":
|
||||
return page_offer(self, url_split, post_string)
|
||||
if page == 'offers':
|
||||
if page == "offers":
|
||||
return page_offers(self, url_split, post_string)
|
||||
if page == 'newoffer':
|
||||
if page == "newoffer":
|
||||
return page_newoffer(self, url_split, post_string)
|
||||
if page == 'sentoffers':
|
||||
if page == "sentoffers":
|
||||
return page_offers(self, url_split, post_string, sent=True)
|
||||
if page == 'bid':
|
||||
if page == "bid":
|
||||
return page_bid(self, url_split, post_string)
|
||||
if page == 'receivedbids':
|
||||
if page == "receivedbids":
|
||||
return page_bids(self, url_split, post_string, received=True)
|
||||
if page == 'sentbids':
|
||||
if page == "sentbids":
|
||||
return page_bids(self, url_split, post_string, sent=True)
|
||||
if page == 'availablebids':
|
||||
if page == "availablebids":
|
||||
return page_bids(self, url_split, post_string, available=True)
|
||||
if page == 'watched':
|
||||
if page == "watched":
|
||||
return self.page_watched(url_split, post_string)
|
||||
if page == 'smsgaddresses':
|
||||
if page == "smsgaddresses":
|
||||
return page_smsgaddresses(self, url_split, post_string)
|
||||
if page == 'identity':
|
||||
if page == "identity":
|
||||
return page_identity(self, url_split, post_string)
|
||||
if page == 'tor':
|
||||
if page == "tor":
|
||||
return page_tor(self, url_split, post_string)
|
||||
if page == 'automation':
|
||||
if page == "automation":
|
||||
return page_automation_strategies(self, url_split, post_string)
|
||||
if page == 'automationstrategy':
|
||||
if page == "automationstrategy":
|
||||
return page_automation_strategy(self, url_split, post_string)
|
||||
if page == 'newautomationstrategy':
|
||||
if page == "newautomationstrategy":
|
||||
return page_automation_strategy_new(self, url_split, post_string)
|
||||
if page == 'shutdown':
|
||||
if page == "shutdown":
|
||||
return self.page_shutdown(url_split, post_string)
|
||||
if page == 'changepassword':
|
||||
if page == "changepassword":
|
||||
return page_changepassword(self, url_split, post_string)
|
||||
if page == 'unlock':
|
||||
if page == "unlock":
|
||||
return page_unlock(self, url_split, post_string)
|
||||
if page == 'lock':
|
||||
if page == "lock":
|
||||
return page_lock(self, url_split, post_string)
|
||||
if page != '':
|
||||
if page != "":
|
||||
return self.page_404(url_split)
|
||||
return self.page_index(url_split)
|
||||
except LockedCoinError:
|
||||
|
@ -563,20 +621,20 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
self.wfile.write(response)
|
||||
|
||||
def do_POST(self):
|
||||
post_string = self.rfile.read(int(self.headers.get('Content-Length')))
|
||||
post_string = self.rfile.read(int(self.headers.get("Content-Length")))
|
||||
|
||||
is_json = True if 'json' in self.headers.get('Content-Type', '') else False
|
||||
is_json = True if "json" in self.headers.get("Content-Type", "") else False
|
||||
response = self.handle_http(200, self.path, post_string, is_json)
|
||||
self.wfile.write(response)
|
||||
|
||||
def do_HEAD(self):
|
||||
self.putHeaders(200, 'text/html')
|
||||
self.putHeaders(200, "text/html")
|
||||
|
||||
def do_OPTIONS(self):
|
||||
self.send_response(200, 'ok')
|
||||
self.send_response(200, "ok")
|
||||
if self.server.allow_cors:
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.send_header('Access-Control-Allow-Headers', '*')
|
||||
self.send_header("Access-Control-Allow-Origin", "*")
|
||||
self.send_header("Access-Control-Allow-Headers", "*")
|
||||
self.end_headers()
|
||||
|
||||
|
||||
|
@ -590,7 +648,7 @@ class HttpThread(threading.Thread, HTTPServer):
|
|||
self.port_no = port_no
|
||||
self.allow_cors = allow_cors
|
||||
self.swap_client = swap_client
|
||||
self.title = 'BasicSwap - ' + __version__
|
||||
self.title = "BasicSwap - " + __version__
|
||||
self.last_form_id = dict()
|
||||
self.session_tokens = dict()
|
||||
self.env = env
|
||||
|
@ -605,9 +663,9 @@ class HttpThread(threading.Thread, HTTPServer):
|
|||
# Send fake request
|
||||
conn = http.client.HTTPConnection(self.host_name, self.port_no)
|
||||
conn.connect()
|
||||
conn.request('GET', '/none')
|
||||
conn.request("GET", "/none")
|
||||
response = conn.getresponse()
|
||||
data = response.read()
|
||||
_ = response.read()
|
||||
conn.close()
|
||||
|
||||
def serve_forever(self):
|
||||
|
|
|
@ -14,7 +14,8 @@ from basicswap.chainparams import (
|
|||
)
|
||||
from basicswap.util import (
|
||||
ensure,
|
||||
i2b, b2i,
|
||||
i2b,
|
||||
b2i,
|
||||
make_int,
|
||||
format_amount,
|
||||
TemporaryError,
|
||||
|
@ -26,9 +27,7 @@ from basicswap.util.ecc import (
|
|||
ep,
|
||||
getSecretInt,
|
||||
)
|
||||
from coincurve.dleag import (
|
||||
verify_secp256k1_point
|
||||
)
|
||||
from coincurve.dleag import verify_secp256k1_point
|
||||
from coincurve.keys import (
|
||||
PublicKey,
|
||||
)
|
||||
|
@ -67,33 +66,33 @@ class CoinInterface:
|
|||
|
||||
def coin_name(self) -> str:
|
||||
coin_chainparams = chainparams[self.coin_type()]
|
||||
if 'display_name' in coin_chainparams:
|
||||
return coin_chainparams['display_name']
|
||||
return coin_chainparams['name'].capitalize()
|
||||
if "display_name" in coin_chainparams:
|
||||
return coin_chainparams["display_name"]
|
||||
return coin_chainparams["name"].capitalize()
|
||||
|
||||
def ticker(self) -> str:
|
||||
ticker = chainparams[self.coin_type()]['ticker']
|
||||
if self._network == 'testnet':
|
||||
ticker = 't' + ticker
|
||||
elif self._network == 'regtest':
|
||||
ticker = 'rt' + ticker
|
||||
ticker = chainparams[self.coin_type()]["ticker"]
|
||||
if self._network == "testnet":
|
||||
ticker = "t" + ticker
|
||||
elif self._network == "regtest":
|
||||
ticker = "rt" + ticker
|
||||
return ticker
|
||||
|
||||
def getExchangeTicker(self, exchange_name: str) -> str:
|
||||
return chainparams[self.coin_type()]['ticker']
|
||||
return chainparams[self.coin_type()]["ticker"]
|
||||
|
||||
def getExchangeName(self, exchange_name: str) -> str:
|
||||
return chainparams[self.coin_type()]['name']
|
||||
return chainparams[self.coin_type()]["name"]
|
||||
|
||||
def ticker_mainnet(self) -> str:
|
||||
ticker = chainparams[self.coin_type()]['ticker']
|
||||
ticker = chainparams[self.coin_type()]["ticker"]
|
||||
return ticker
|
||||
|
||||
def min_amount(self) -> int:
|
||||
return chainparams[self.coin_type()][self._network]['min_amount']
|
||||
return chainparams[self.coin_type()][self._network]["min_amount"]
|
||||
|
||||
def max_amount(self) -> int:
|
||||
return chainparams[self.coin_type()][self._network]['max_amount']
|
||||
return chainparams[self.coin_type()][self._network]["max_amount"]
|
||||
|
||||
def setWalletSeedWarning(self, value: bool) -> None:
|
||||
self._unknown_wallet_seed = value
|
||||
|
@ -111,7 +110,7 @@ class CoinInterface:
|
|||
return chainparams[self.coin_type()][self._network]
|
||||
|
||||
def has_segwit(self) -> bool:
|
||||
return chainparams[self.coin_type()].get('has_segwit', True)
|
||||
return chainparams[self.coin_type()].get("has_segwit", True)
|
||||
|
||||
def use_p2shp2wsh(self) -> bool:
|
||||
# p2sh-p2wsh
|
||||
|
@ -121,24 +120,26 @@ class CoinInterface:
|
|||
if isinstance(ex, TemporaryError):
|
||||
return True
|
||||
str_error: str = str(ex).lower()
|
||||
if 'not enough unlocked money' in str_error:
|
||||
if "not enough unlocked money" in str_error:
|
||||
return True
|
||||
if 'no unlocked balance' in str_error:
|
||||
if "no unlocked balance" in str_error:
|
||||
return True
|
||||
if 'transaction was rejected by daemon' in str_error:
|
||||
if "transaction was rejected by daemon" in str_error:
|
||||
return True
|
||||
if 'invalid unlocked_balance' in str_error:
|
||||
if "invalid unlocked_balance" in str_error:
|
||||
return True
|
||||
if 'daemon is busy' in str_error:
|
||||
if "daemon is busy" in str_error:
|
||||
return True
|
||||
if 'timed out' in str_error:
|
||||
if "timed out" in str_error:
|
||||
return True
|
||||
if 'request-sent' in str_error:
|
||||
if "request-sent" in str_error:
|
||||
return True
|
||||
return False
|
||||
|
||||
def setConfTarget(self, new_conf_target: int) -> None:
|
||||
ensure(new_conf_target >= 1 and new_conf_target < 33, 'Invalid conf_target value')
|
||||
ensure(
|
||||
new_conf_target >= 1 and new_conf_target < 33, "Invalid conf_target value"
|
||||
)
|
||||
self._conf_target = new_conf_target
|
||||
|
||||
def walletRestoreHeight(self) -> int:
|
||||
|
@ -171,30 +172,15 @@ class CoinInterface:
|
|||
return self._altruistic
|
||||
|
||||
|
||||
class AdaptorSigInterface():
|
||||
class AdaptorSigInterface:
|
||||
def getScriptLockTxDummyWitness(self, script: bytes):
|
||||
return [
|
||||
b'',
|
||||
bytes(72),
|
||||
bytes(72),
|
||||
bytes(len(script))
|
||||
]
|
||||
return [b"", bytes(72), bytes(72), bytes(len(script))]
|
||||
|
||||
def getScriptLockRefundSpendTxDummyWitness(self, script: bytes):
|
||||
return [
|
||||
b'',
|
||||
bytes(72),
|
||||
bytes(72),
|
||||
bytes((1,)),
|
||||
bytes(len(script))
|
||||
]
|
||||
return [b"", bytes(72), bytes(72), bytes((1,)), bytes(len(script))]
|
||||
|
||||
def getScriptLockRefundSwipeTxDummyWitness(self, script: bytes):
|
||||
return [
|
||||
bytes(72),
|
||||
b'',
|
||||
bytes(len(script))
|
||||
]
|
||||
return [bytes(72), b"", bytes(len(script))]
|
||||
|
||||
|
||||
class Secp256k1Interface(CoinInterface, AdaptorSigInterface):
|
||||
|
@ -213,7 +199,7 @@ class Secp256k1Interface(CoinInterface, AdaptorSigInterface):
|
|||
|
||||
def verifyKey(self, k: bytes) -> bool:
|
||||
i = b2i(k)
|
||||
return (i < ep.o and i > 0)
|
||||
return i < ep.o and i > 0
|
||||
|
||||
def verifyPubkey(self, pubkey_bytes: bytes) -> bool:
|
||||
return verify_secp256k1_point(pubkey_bytes)
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -2,6 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022-2024 tecnovert
|
||||
# Copyright (c) 2024 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -11,7 +12,10 @@ from basicswap.util.address import decodeAddress
|
|||
from basicswap.contrib.mnemonic import Mnemonic
|
||||
from basicswap.contrib.test_framework.script import (
|
||||
CScript,
|
||||
OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG
|
||||
OP_DUP,
|
||||
OP_HASH160,
|
||||
OP_EQUALVERIFY,
|
||||
OP_CHECKSIG,
|
||||
)
|
||||
|
||||
|
||||
|
@ -22,41 +26,49 @@ class DASHInterface(BTCInterface):
|
|||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super().__init__(coin_settings, network, swap_client)
|
||||
self._wallet_passphrase = ''
|
||||
self._wallet_passphrase = ""
|
||||
self._have_checked_seed = False
|
||||
|
||||
self._wallet_v20_compatible = False if not swap_client else swap_client.getChainClientSettings(self.coin_type()).get('wallet_v20_compatible', False)
|
||||
self._wallet_v20_compatible = (
|
||||
False
|
||||
if not swap_client
|
||||
else swap_client.getChainClientSettings(self.coin_type()).get(
|
||||
"wallet_v20_compatible", False
|
||||
)
|
||||
)
|
||||
|
||||
def decodeAddress(self, address: str) -> bytes:
|
||||
return decodeAddress(address)[1:]
|
||||
|
||||
def getWalletSeedID(self) -> str:
|
||||
hdseed: str = self.rpc_wallet('dumphdinfo')['hdseed']
|
||||
hdseed: str = self.rpc_wallet("dumphdinfo")["hdseed"]
|
||||
return self.getSeedHash(bytes.fromhex(hdseed)).hex()
|
||||
|
||||
def entropyToMnemonic(self, key: bytes) -> None:
|
||||
return Mnemonic('english').to_mnemonic(key)
|
||||
return Mnemonic("english").to_mnemonic(key)
|
||||
|
||||
def initialiseWallet(self, key_bytes: bytes) -> None:
|
||||
self._have_checked_seed = False
|
||||
if self._wallet_v20_compatible:
|
||||
self._log.warning('Generating wallet compatible with v20 seed.')
|
||||
self._log.warning("Generating wallet compatible with v20 seed.")
|
||||
words = self.entropyToMnemonic(key_bytes)
|
||||
mnemonic_passphrase = ''
|
||||
self.rpc_wallet('upgradetohd', [words, mnemonic_passphrase, self._wallet_passphrase])
|
||||
mnemonic_passphrase = ""
|
||||
self.rpc_wallet(
|
||||
"upgradetohd", [words, mnemonic_passphrase, self._wallet_passphrase]
|
||||
)
|
||||
self._have_checked_seed = False
|
||||
if self._wallet_passphrase != '':
|
||||
if self._wallet_passphrase != "":
|
||||
self.unlockWallet(self._wallet_passphrase)
|
||||
return
|
||||
|
||||
key_wif = self.encodeKey(key_bytes)
|
||||
self.rpc_wallet('sethdseed', [True, key_wif])
|
||||
self.rpc_wallet("sethdseed", [True, key_wif])
|
||||
|
||||
def checkExpectedSeed(self, expect_seedid: str) -> bool:
|
||||
self._expect_seedid_hex = expect_seedid
|
||||
rv = self.rpc_wallet('dumphdinfo')
|
||||
if rv['mnemonic'] != '':
|
||||
entropy = Mnemonic('english').to_entropy(rv['mnemonic'].split(' '))
|
||||
rv = self.rpc_wallet("dumphdinfo")
|
||||
if rv["mnemonic"] != "":
|
||||
entropy = Mnemonic("english").to_entropy(rv["mnemonic"].split(" "))
|
||||
entropy_hash = self.getAddressHashFromKey(entropy)[::-1].hex()
|
||||
have_expected_seed: bool = expect_seedid == entropy_hash
|
||||
else:
|
||||
|
@ -65,11 +77,11 @@ class DASHInterface(BTCInterface):
|
|||
return have_expected_seed
|
||||
|
||||
def withdrawCoin(self, value, addr_to, subfee):
|
||||
params = [addr_to, value, '', '', subfee, False, False, self._conf_target]
|
||||
return self.rpc_wallet('sendtoaddress', params)
|
||||
params = [addr_to, value, "", "", subfee, False, False, self._conf_target]
|
||||
return self.rpc_wallet("sendtoaddress", params)
|
||||
|
||||
def getSpendableBalance(self) -> int:
|
||||
return self.make_int(self.rpc_wallet('getwalletinfo')['balance'])
|
||||
return self.make_int(self.rpc_wallet("getwalletinfo")["balance"])
|
||||
|
||||
def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
|
||||
# Return P2PKH
|
||||
|
@ -79,19 +91,23 @@ class DASHInterface(BTCInterface):
|
|||
add_bytes = 107
|
||||
size = len(tx.serialize_with_witness()) + add_bytes
|
||||
pay_fee = round(fee_rate * size / 1000)
|
||||
self._log.info(f'BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
|
||||
self._log.info(
|
||||
f"BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}."
|
||||
)
|
||||
return pay_fee
|
||||
|
||||
def findTxnByHash(self, txid_hex: str):
|
||||
# Only works for wallet txns
|
||||
try:
|
||||
rv = self.rpc_wallet('gettransaction', [txid_hex])
|
||||
except Exception as ex:
|
||||
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
|
||||
rv = self.rpc_wallet("gettransaction", [txid_hex])
|
||||
except Exception as e: # noqa: F841
|
||||
self._log.debug(
|
||||
"findTxnByHash getrawtransaction failed: {}".format(txid_hex)
|
||||
)
|
||||
return None
|
||||
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
|
||||
block_height = self.getBlockHeader(rv['blockhash'])['height']
|
||||
return {'txid': txid_hex, 'amount': 0, 'height': block_height}
|
||||
if "confirmations" in rv and rv["confirmations"] >= self.blocks_confirmed:
|
||||
block_height = self.getBlockHeader(rv["blockhash"])["height"]
|
||||
return {"txid": txid_hex, "amount": 0, "height": block_height}
|
||||
|
||||
return None
|
||||
|
||||
|
@ -105,8 +121,8 @@ class DASHInterface(BTCInterface):
|
|||
self._sc.checkWalletSeed(self.coin_type())
|
||||
except Exception as ex:
|
||||
# dumphdinfo can fail if the wallet is not initialised
|
||||
self._log.debug(f'DASH checkWalletSeed failed: {ex}.')
|
||||
self._log.debug(f"DASH checkWalletSeed failed: {ex}.")
|
||||
|
||||
def lockWallet(self):
|
||||
super().lockWallet()
|
||||
self._wallet_passphrase = ''
|
||||
self._wallet_passphrase = ""
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
from .dcr import DCRInterface
|
||||
|
||||
__all__ = ['DCRInterface',]
|
||||
__all__ = [
|
||||
"DCRInterface",
|
||||
]
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -23,7 +23,7 @@ class SigHashType(IntEnum):
|
|||
SigHashSingle = 0x3
|
||||
SigHashAnyOneCanPay = 0x80
|
||||
|
||||
SigHashMask = 0x1f
|
||||
SigHashMask = 0x1F
|
||||
|
||||
|
||||
class SignatureType(IntEnum):
|
||||
|
@ -33,7 +33,7 @@ class SignatureType(IntEnum):
|
|||
|
||||
|
||||
class COutPoint:
|
||||
__slots__ = ('hash', 'n', 'tree')
|
||||
__slots__ = ("hash", "n", "tree")
|
||||
|
||||
def __init__(self, hash=0, n=0, tree=0):
|
||||
self.hash = hash
|
||||
|
@ -41,24 +41,30 @@ class COutPoint:
|
|||
self.tree = tree
|
||||
|
||||
def get_hash(self) -> bytes:
|
||||
return self.hash.to_bytes(32, 'big')
|
||||
return self.hash.to_bytes(32, "big")
|
||||
|
||||
|
||||
class CTxIn:
|
||||
__slots__ = ('prevout', 'sequence',
|
||||
'value_in', 'block_height', 'block_index', 'signature_script') # Witness
|
||||
__slots__ = (
|
||||
"prevout",
|
||||
"sequence",
|
||||
"value_in",
|
||||
"block_height",
|
||||
"block_index",
|
||||
"signature_script",
|
||||
) # Witness
|
||||
|
||||
def __init__(self, prevout=COutPoint(), sequence=0):
|
||||
self.prevout = prevout
|
||||
self.sequence = sequence
|
||||
self.value_in = -1
|
||||
self.block_height = 0
|
||||
self.block_index = 0xffffffff
|
||||
self.block_index = 0xFFFFFFFF
|
||||
self.signature_script = bytes()
|
||||
|
||||
|
||||
class CTxOut:
|
||||
__slots__ = ('value', 'version', 'script_pubkey')
|
||||
__slots__ = ("value", "version", "script_pubkey")
|
||||
|
||||
def __init__(self, value=0, script_pubkey=bytes()):
|
||||
self.value = value
|
||||
|
@ -67,7 +73,7 @@ class CTxOut:
|
|||
|
||||
|
||||
class CTransaction:
|
||||
__slots__ = ('hash', 'version', 'vin', 'vout', 'locktime', 'expiry')
|
||||
__slots__ = ("hash", "version", "vin", "vout", "locktime", "expiry")
|
||||
|
||||
def __init__(self, tx=None):
|
||||
if tx is None:
|
||||
|
@ -85,8 +91,8 @@ class CTransaction:
|
|||
|
||||
def deserialize(self, data: bytes) -> None:
|
||||
|
||||
version = int.from_bytes(data[:4], 'little')
|
||||
self.version = version & 0xffff
|
||||
version = int.from_bytes(data[:4], "little")
|
||||
self.version = version & 0xFFFF
|
||||
ser_type: int = version >> 16
|
||||
o = 4
|
||||
|
||||
|
@ -97,13 +103,13 @@ class CTransaction:
|
|||
for i in range(num_txin):
|
||||
txi = CTxIn()
|
||||
txi.prevout = COutPoint()
|
||||
txi.prevout.hash = int.from_bytes(data[o:o + 32], 'little')
|
||||
txi.prevout.hash = int.from_bytes(data[o : o + 32], "little")
|
||||
o += 32
|
||||
txi.prevout.n = int.from_bytes(data[o:o + 4], 'little')
|
||||
txi.prevout.n = int.from_bytes(data[o : o + 4], "little")
|
||||
o += 4
|
||||
txi.prevout.tree = data[o]
|
||||
o += 1
|
||||
txi.sequence = int.from_bytes(data[o:o + 4], 'little')
|
||||
txi.sequence = int.from_bytes(data[o : o + 4], "little")
|
||||
o += 4
|
||||
self.vin.append(txi)
|
||||
|
||||
|
@ -112,19 +118,19 @@ class CTransaction:
|
|||
|
||||
for i in range(num_txout):
|
||||
txo = CTxOut()
|
||||
txo.value = int.from_bytes(data[o:o + 8], 'little')
|
||||
txo.value = int.from_bytes(data[o : o + 8], "little")
|
||||
o += 8
|
||||
txo.version = int.from_bytes(data[o:o + 2], 'little')
|
||||
txo.version = int.from_bytes(data[o : o + 2], "little")
|
||||
o += 2
|
||||
script_bytes, nb = decode_compactsize(data, o)
|
||||
o += nb
|
||||
txo.script_pubkey = data[o:o + script_bytes]
|
||||
txo.script_pubkey = data[o : o + script_bytes]
|
||||
o += script_bytes
|
||||
self.vout.append(txo)
|
||||
|
||||
self.locktime = int.from_bytes(data[o:o + 4], 'little')
|
||||
self.locktime = int.from_bytes(data[o : o + 4], "little")
|
||||
o += 4
|
||||
self.expiry = int.from_bytes(data[o:o + 4], 'little')
|
||||
self.expiry = int.from_bytes(data[o : o + 4], "little")
|
||||
o += 4
|
||||
|
||||
if ser_type == TxSerializeType.NoWitness:
|
||||
|
@ -137,51 +143,53 @@ class CTransaction:
|
|||
self.vin = [CTxIn() for _ in range(num_wit_scripts)]
|
||||
else:
|
||||
if num_wit_scripts != len(self.vin):
|
||||
raise ValueError('non equal witness and prefix txin quantities')
|
||||
raise ValueError("non equal witness and prefix txin quantities")
|
||||
|
||||
for i in range(num_wit_scripts):
|
||||
txi = self.vin[i]
|
||||
txi.value_in = int.from_bytes(data[o:o + 8], 'little')
|
||||
txi.value_in = int.from_bytes(data[o : o + 8], "little")
|
||||
o += 8
|
||||
txi.block_height = int.from_bytes(data[o:o + 4], 'little')
|
||||
txi.block_height = int.from_bytes(data[o : o + 4], "little")
|
||||
o += 4
|
||||
txi.block_index = int.from_bytes(data[o:o + 4], 'little')
|
||||
txi.block_index = int.from_bytes(data[o : o + 4], "little")
|
||||
o += 4
|
||||
script_bytes, nb = decode_compactsize(data, o)
|
||||
o += nb
|
||||
txi.signature_script = data[o:o + script_bytes]
|
||||
txi.signature_script = data[o : o + script_bytes]
|
||||
o += script_bytes
|
||||
|
||||
def serialize(self, ser_type=TxSerializeType.Full) -> bytes:
|
||||
data = bytes()
|
||||
version = (self.version & 0xffff) | (ser_type << 16)
|
||||
data += version.to_bytes(4, 'little')
|
||||
version = (self.version & 0xFFFF) | (ser_type << 16)
|
||||
data += version.to_bytes(4, "little")
|
||||
|
||||
if ser_type == TxSerializeType.Full or ser_type == TxSerializeType.NoWitness:
|
||||
data += encode_compactsize(len(self.vin))
|
||||
for txi in self.vin:
|
||||
data += txi.prevout.hash.to_bytes(32, 'little')
|
||||
data += txi.prevout.n.to_bytes(4, 'little')
|
||||
data += txi.prevout.tree.to_bytes(1, 'little')
|
||||
data += txi.sequence.to_bytes(4, 'little')
|
||||
data += txi.prevout.hash.to_bytes(32, "little")
|
||||
data += txi.prevout.n.to_bytes(4, "little")
|
||||
data += txi.prevout.tree.to_bytes(1, "little")
|
||||
data += txi.sequence.to_bytes(4, "little")
|
||||
|
||||
data += encode_compactsize(len(self.vout))
|
||||
for txo in self.vout:
|
||||
data += txo.value.to_bytes(8, 'little')
|
||||
data += txo.version.to_bytes(2, 'little')
|
||||
data += txo.value.to_bytes(8, "little")
|
||||
data += txo.version.to_bytes(2, "little")
|
||||
data += encode_compactsize(len(txo.script_pubkey))
|
||||
data += txo.script_pubkey
|
||||
|
||||
data += self.locktime.to_bytes(4, 'little')
|
||||
data += self.expiry.to_bytes(4, 'little')
|
||||
data += self.locktime.to_bytes(4, "little")
|
||||
data += self.expiry.to_bytes(4, "little")
|
||||
|
||||
if ser_type == TxSerializeType.Full or ser_type == TxSerializeType.OnlyWitness:
|
||||
data += encode_compactsize(len(self.vin))
|
||||
for txi in self.vin:
|
||||
tc_value_in = txi.value_in & 0xffffffffffffffff # Convert negative values
|
||||
data += tc_value_in.to_bytes(8, 'little')
|
||||
data += txi.block_height.to_bytes(4, 'little')
|
||||
data += txi.block_index.to_bytes(4, 'little')
|
||||
tc_value_in = (
|
||||
txi.value_in & 0xFFFFFFFFFFFFFFFF
|
||||
) # Convert negative values
|
||||
data += tc_value_in.to_bytes(8, "little")
|
||||
data += txi.block_height.to_bytes(4, "little")
|
||||
data += txi.block_index.to_bytes(4, "little")
|
||||
data += encode_compactsize(len(txi.signature_script))
|
||||
data += txi.signature_script
|
||||
|
||||
|
@ -191,10 +199,10 @@ class CTransaction:
|
|||
return blake256(self.serialize(TxSerializeType.NoWitness))[::-1]
|
||||
|
||||
def TxHashWitness(self) -> bytes:
|
||||
raise ValueError('todo')
|
||||
raise ValueError("todo")
|
||||
|
||||
def TxHashFull(self) -> bytes:
|
||||
raise ValueError('todo')
|
||||
raise ValueError("todo")
|
||||
|
||||
|
||||
def findOutput(tx, script_pk: bytes):
|
||||
|
|
|
@ -9,34 +9,34 @@ import traceback
|
|||
from basicswap.rpc import Jsonrpc
|
||||
|
||||
|
||||
def callrpc(rpc_port, auth, method, params=[], host='127.0.0.1'):
|
||||
def callrpc(rpc_port, auth, method, params=[], host="127.0.0.1"):
|
||||
try:
|
||||
url = 'http://{}@{}:{}/'.format(auth, host, rpc_port)
|
||||
url = "http://{}@{}:{}/".format(auth, host, rpc_port)
|
||||
x = Jsonrpc(url)
|
||||
x.__handler = None
|
||||
v = x.json_request(method, params)
|
||||
x.close()
|
||||
r = json.loads(v.decode('utf-8'))
|
||||
r = json.loads(v.decode("utf-8"))
|
||||
except Exception as ex:
|
||||
traceback.print_exc()
|
||||
raise ValueError('RPC server error ' + str(ex) + ', method: ' + method)
|
||||
raise ValueError("RPC server error " + str(ex) + ", method: " + method)
|
||||
|
||||
if 'error' in r and r['error'] is not None:
|
||||
raise ValueError('RPC error ' + str(r['error']))
|
||||
if "error" in r and r["error"] is not None:
|
||||
raise ValueError("RPC error " + str(r["error"]))
|
||||
|
||||
return r['result']
|
||||
return r["result"]
|
||||
|
||||
|
||||
def openrpc(rpc_port, auth, host='127.0.0.1'):
|
||||
def openrpc(rpc_port, auth, host="127.0.0.1"):
|
||||
try:
|
||||
url = 'http://{}@{}:{}/'.format(auth, host, rpc_port)
|
||||
url = "http://{}@{}:{}/".format(auth, host, rpc_port)
|
||||
return Jsonrpc(url)
|
||||
except Exception as ex:
|
||||
traceback.print_exc()
|
||||
raise ValueError('RPC error ' + str(ex))
|
||||
raise ValueError("RPC error " + str(ex))
|
||||
|
||||
|
||||
def make_rpc_func(port, auth, host='127.0.0.1'):
|
||||
def make_rpc_func(port, auth, host="127.0.0.1"):
|
||||
port = port
|
||||
auth = auth
|
||||
host = host
|
||||
|
@ -44,4 +44,5 @@ def make_rpc_func(port, auth, host='127.0.0.1'):
|
|||
def rpc_func(method, params=None):
|
||||
nonlocal port, auth, host
|
||||
return callrpc(port, auth, method, params, host)
|
||||
|
||||
return rpc_func
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
OP_0 = 0x00
|
||||
OP_DATA_1 = 0x01
|
||||
OP_1NEGATE = 0x4f
|
||||
OP_1NEGATE = 0x4F
|
||||
OP_1 = 0x51
|
||||
OP_IF = 0x63
|
||||
OP_ELSE = 0x67
|
||||
|
@ -16,13 +16,13 @@ OP_DROP = 0x75
|
|||
OP_DUP = 0x76
|
||||
OP_EQUAL = 0x87
|
||||
OP_EQUALVERIFY = 0x88
|
||||
OP_PUSHDATA1 = 0x4c
|
||||
OP_PUSHDATA2 = 0x4d
|
||||
OP_PUSHDATA4 = 0x4e
|
||||
OP_HASH160 = 0xa9
|
||||
OP_CHECKSIG = 0xac
|
||||
OP_CHECKMULTISIG = 0xae
|
||||
OP_CHECKSEQUENCEVERIFY = 0xb2
|
||||
OP_PUSHDATA1 = 0x4C
|
||||
OP_PUSHDATA2 = 0x4D
|
||||
OP_PUSHDATA4 = 0x4E
|
||||
OP_HASH160 = 0xA9
|
||||
OP_CHECKSIG = 0xAC
|
||||
OP_CHECKMULTISIG = 0xAE
|
||||
OP_CHECKSEQUENCEVERIFY = 0xB2
|
||||
|
||||
|
||||
def push_script_data(data_array: bytearray, data: bytes) -> None:
|
||||
|
@ -39,12 +39,12 @@ def push_script_data(data_array: bytearray, data: bytes) -> None:
|
|||
return
|
||||
|
||||
if len_data < OP_PUSHDATA1:
|
||||
data_array += len_data.to_bytes(1, 'little')
|
||||
elif len_data <= 0xff:
|
||||
data_array += len_data.to_bytes(1, "little")
|
||||
elif len_data <= 0xFF:
|
||||
data_array += bytes((OP_PUSHDATA1, len_data))
|
||||
elif len_data <= 0xffff:
|
||||
data_array += bytes((OP_PUSHDATA2,)) + len_data.to_bytes(2, 'little')
|
||||
elif len_data <= 0xFFFF:
|
||||
data_array += bytes((OP_PUSHDATA2,)) + len_data.to_bytes(2, "little")
|
||||
else:
|
||||
data_array += bytes((OP_PUSHDATA4,)) + len_data.to_bytes(4, 'little')
|
||||
data_array += bytes((OP_PUSHDATA4,)) + len_data.to_bytes(4, "little")
|
||||
|
||||
data_array += data
|
||||
|
|
|
@ -10,46 +10,48 @@ import subprocess
|
|||
|
||||
|
||||
def createDCRWallet(args, hex_seed, logging, delay_event):
|
||||
logging.info('Creating DCR wallet')
|
||||
logging.info("Creating DCR wallet")
|
||||
|
||||
(pipe_r, pipe_w) = os.pipe() # subprocess.PIPE is buffered, blocks when read
|
||||
|
||||
if os.name == 'nt':
|
||||
str_args = ' '.join(args)
|
||||
p = subprocess.Popen(str_args, shell=True, stdin=subprocess.PIPE, stdout=pipe_w, stderr=pipe_w)
|
||||
if os.name == "nt":
|
||||
str_args = " ".join(args)
|
||||
p = subprocess.Popen(
|
||||
str_args, shell=True, stdin=subprocess.PIPE, stdout=pipe_w, stderr=pipe_w
|
||||
)
|
||||
else:
|
||||
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=pipe_w, stderr=pipe_w)
|
||||
|
||||
def readOutput():
|
||||
buf = os.read(pipe_r, 1024).decode('utf-8')
|
||||
buf = os.read(pipe_r, 1024).decode("utf-8")
|
||||
response = None
|
||||
if 'Opened wallet' in buf:
|
||||
if "Opened wallet" in buf:
|
||||
pass
|
||||
elif 'Use the existing configured private passphrase' in buf:
|
||||
response = b'y\n'
|
||||
elif 'Do you want to add an additional layer of encryption' in buf:
|
||||
response = b'n\n'
|
||||
elif 'Do you have an existing wallet seed' in buf:
|
||||
response = b'y\n'
|
||||
elif 'Enter existing wallet seed' in buf:
|
||||
response = (hex_seed + '\n').encode('utf-8')
|
||||
elif 'Seed input successful' in buf:
|
||||
elif "Use the existing configured private passphrase" in buf:
|
||||
response = b"y\n"
|
||||
elif "Do you want to add an additional layer of encryption" in buf:
|
||||
response = b"n\n"
|
||||
elif "Do you have an existing wallet seed" in buf:
|
||||
response = b"y\n"
|
||||
elif "Enter existing wallet seed" in buf:
|
||||
response = (hex_seed + "\n").encode("utf-8")
|
||||
elif "Seed input successful" in buf:
|
||||
pass
|
||||
elif 'Upgrading database from version' in buf:
|
||||
elif "Upgrading database from version" in buf:
|
||||
pass
|
||||
elif 'Ticket commitments db upgrade done' in buf:
|
||||
elif "Ticket commitments db upgrade done" in buf:
|
||||
pass
|
||||
elif 'The wallet has been created successfully' in buf:
|
||||
elif "The wallet has been created successfully" in buf:
|
||||
pass
|
||||
else:
|
||||
raise ValueError(f'Unexpected output: {buf}')
|
||||
raise ValueError(f"Unexpected output: {buf}")
|
||||
if response is not None:
|
||||
p.stdin.write(response)
|
||||
p.stdin.flush()
|
||||
|
||||
try:
|
||||
while p.poll() is None:
|
||||
if os.name == 'nt':
|
||||
if os.name == "nt":
|
||||
readOutput()
|
||||
delay_event.wait(0.1)
|
||||
continue
|
||||
|
@ -57,7 +59,7 @@ def createDCRWallet(args, hex_seed, logging, delay_event):
|
|||
readOutput()
|
||||
delay_event.wait(0.1)
|
||||
except Exception as e:
|
||||
logging.error(f'dcrwallet --create failed: {e}')
|
||||
logging.error(f"dcrwallet --create failed: {e}")
|
||||
finally:
|
||||
if p.poll() is None:
|
||||
p.terminate()
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022-2023 tecnovert
|
||||
# Copyright (c) 2024 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -40,10 +41,12 @@ class FIROInterface(BTCInterface):
|
|||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(FIROInterface, self).__init__(coin_settings, network, swap_client)
|
||||
# No multiwallet support
|
||||
self.rpc_wallet = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host)
|
||||
self.rpc_wallet = make_rpc_func(
|
||||
self._rpcport, self._rpcauth, host=self._rpc_host
|
||||
)
|
||||
|
||||
def getExchangeName(self, exchange_name: str) -> str:
|
||||
return 'zcoin'
|
||||
return "zcoin"
|
||||
|
||||
def initialiseWallet(self, key):
|
||||
# load with -hdseed= parameter
|
||||
|
@ -52,8 +55,8 @@ class FIROInterface(BTCInterface):
|
|||
def checkWallets(self) -> int:
|
||||
return 1
|
||||
|
||||
def getNewAddress(self, use_segwit, label='swap_receive'):
|
||||
return self.rpc('getnewaddress', [label])
|
||||
def getNewAddress(self, use_segwit, label="swap_receive"):
|
||||
return self.rpc("getnewaddress", [label])
|
||||
# addr_plain = self.rpc('getnewaddress', [label])
|
||||
# return self.rpc('addwitnessaddress', [addr_plain])
|
||||
|
||||
|
@ -61,20 +64,20 @@ class FIROInterface(BTCInterface):
|
|||
return decodeAddress(address)[1:]
|
||||
|
||||
def encodeSegwitAddress(self, script):
|
||||
raise ValueError('TODO')
|
||||
raise ValueError("TODO")
|
||||
|
||||
def decodeSegwitAddress(self, addr):
|
||||
raise ValueError('TODO')
|
||||
raise ValueError("TODO")
|
||||
|
||||
def isWatchOnlyAddress(self, address):
|
||||
addr_info = self.rpc('validateaddress', [address])
|
||||
return addr_info['iswatchonly']
|
||||
addr_info = self.rpc("validateaddress", [address])
|
||||
return addr_info["iswatchonly"]
|
||||
|
||||
def isAddressMine(self, address: str, or_watch_only: bool = False) -> bool:
|
||||
addr_info = self.rpc('validateaddress', [address])
|
||||
addr_info = self.rpc("validateaddress", [address])
|
||||
if not or_watch_only:
|
||||
return addr_info['ismine']
|
||||
return addr_info['ismine'] or addr_info['iswatchonly']
|
||||
return addr_info["ismine"]
|
||||
return addr_info["ismine"] or addr_info["iswatchonly"]
|
||||
|
||||
def getSCLockScriptAddress(self, lock_script: bytes) -> str:
|
||||
lock_tx_dest = self.getScriptDest(lock_script)
|
||||
|
@ -82,58 +85,83 @@ class FIROInterface(BTCInterface):
|
|||
|
||||
if not self.isAddressMine(address, or_watch_only=True):
|
||||
# Expects P2WSH nested in BIP16_P2SH
|
||||
ro = self.rpc('importaddress', [lock_tx_dest.hex(), 'bid lock', False, True])
|
||||
addr_info = self.rpc('validateaddress', [address])
|
||||
self.rpc("importaddress", [lock_tx_dest.hex(), "bid lock", False, True])
|
||||
|
||||
return address
|
||||
|
||||
def getLockTxHeight(self, txid, dest_address, bid_amount, rescan_from, find_index: bool = False, vout: int = -1):
|
||||
def getLockTxHeight(
|
||||
self,
|
||||
txid,
|
||||
dest_address,
|
||||
bid_amount,
|
||||
rescan_from,
|
||||
find_index: bool = False,
|
||||
vout: int = -1,
|
||||
):
|
||||
# Add watchonly address and rescan if required
|
||||
|
||||
if not self.isAddressMine(dest_address, or_watch_only=True):
|
||||
self.importWatchOnlyAddress(dest_address, 'bid')
|
||||
self._log.info('Imported watch-only addr: {}'.format(dest_address))
|
||||
self._log.info('Rescanning {} chain from height: {}'.format(self.coin_name(), rescan_from))
|
||||
self.importWatchOnlyAddress(dest_address, "bid")
|
||||
self._log.info("Imported watch-only addr: {}".format(dest_address))
|
||||
self._log.info(
|
||||
"Rescanning {} chain from height: {}".format(
|
||||
self.coin_name(), rescan_from
|
||||
)
|
||||
)
|
||||
self.rescanBlockchainForAddress(rescan_from, dest_address)
|
||||
|
||||
return_txid = True if txid is None else False
|
||||
if txid is None:
|
||||
txns = self.rpc('listunspent', [0, 9999999, [dest_address, ]])
|
||||
txns = self.rpc(
|
||||
"listunspent",
|
||||
[
|
||||
0,
|
||||
9999999,
|
||||
[
|
||||
dest_address,
|
||||
],
|
||||
],
|
||||
)
|
||||
|
||||
for tx in txns:
|
||||
if self.make_int(tx['amount']) == bid_amount:
|
||||
txid = bytes.fromhex(tx['txid'])
|
||||
if self.make_int(tx["amount"]) == bid_amount:
|
||||
txid = bytes.fromhex(tx["txid"])
|
||||
break
|
||||
|
||||
if txid is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
tx = self.rpc('gettransaction', [txid.hex()])
|
||||
tx = self.rpc("gettransaction", [txid.hex()])
|
||||
|
||||
block_height = 0
|
||||
if 'blockhash' in tx:
|
||||
block_header = self.rpc('getblockheader', [tx['blockhash']])
|
||||
block_height = block_header['height']
|
||||
if "blockhash" in tx:
|
||||
block_header = self.rpc("getblockheader", [tx["blockhash"]])
|
||||
block_height = block_header["height"]
|
||||
|
||||
rv = {
|
||||
'depth': 0 if 'confirmations' not in tx else tx['confirmations'],
|
||||
'height': block_height}
|
||||
"depth": 0 if "confirmations" not in tx else tx["confirmations"],
|
||||
"height": block_height,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
self._log.debug('getLockTxHeight gettransaction failed: %s, %s', txid.hex(), str(e))
|
||||
self._log.debug(
|
||||
"getLockTxHeight gettransaction failed: %s, %s", txid.hex(), str(e)
|
||||
)
|
||||
return None
|
||||
|
||||
if find_index:
|
||||
tx_obj = self.rpc('decoderawtransaction', [tx['hex']])
|
||||
rv['index'] = find_vout_for_address_from_txobj(tx_obj, dest_address)
|
||||
tx_obj = self.rpc("decoderawtransaction", [tx["hex"]])
|
||||
rv["index"] = find_vout_for_address_from_txobj(tx_obj, dest_address)
|
||||
|
||||
if return_txid:
|
||||
rv['txid'] = txid.hex()
|
||||
rv["txid"] = txid.hex()
|
||||
|
||||
return rv
|
||||
|
||||
def createSCLockTx(self, value: int, script: bytearray, vkbv: bytes = None) -> bytes:
|
||||
def createSCLockTx(
|
||||
self, value: int, script: bytearray, vkbv: bytes = None
|
||||
) -> bytes:
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
tx.vout.append(self.txoType()(value, self.getScriptDest(script)))
|
||||
|
@ -144,24 +172,36 @@ class FIROInterface(BTCInterface):
|
|||
return self.fundTx(tx_bytes, feerate)
|
||||
|
||||
def signTxWithWallet(self, tx):
|
||||
rv = self.rpc('signrawtransaction', [tx.hex()])
|
||||
return bytes.fromhex(rv['hex'])
|
||||
rv = self.rpc("signrawtransaction", [tx.hex()])
|
||||
return bytes.fromhex(rv["hex"])
|
||||
|
||||
def createRawFundedTransaction(self, addr_to: str, amount: int, sub_fee: bool = False, lock_unspents: bool = True) -> str:
|
||||
txn = self.rpc('createrawtransaction', [[], {addr_to: self.format_amount(amount)}])
|
||||
def createRawFundedTransaction(
|
||||
self,
|
||||
addr_to: str,
|
||||
amount: int,
|
||||
sub_fee: bool = False,
|
||||
lock_unspents: bool = True,
|
||||
) -> str:
|
||||
txn = self.rpc(
|
||||
"createrawtransaction", [[], {addr_to: self.format_amount(amount)}]
|
||||
)
|
||||
fee_rate, fee_src = self.get_fee_rate(self._conf_target)
|
||||
self._log.debug(f'Fee rate: {fee_rate}, source: {fee_src}, block target: {self._conf_target}')
|
||||
self._log.debug(
|
||||
f"Fee rate: {fee_rate}, source: {fee_src}, block target: {self._conf_target}"
|
||||
)
|
||||
options = {
|
||||
'lockUnspents': lock_unspents,
|
||||
'feeRate': fee_rate,
|
||||
"lockUnspents": lock_unspents,
|
||||
"feeRate": fee_rate,
|
||||
}
|
||||
if sub_fee:
|
||||
options['subtractFeeFromOutputs'] = [0,]
|
||||
return self.rpc('fundrawtransaction', [txn, options])['hex']
|
||||
options["subtractFeeFromOutputs"] = [
|
||||
0,
|
||||
]
|
||||
return self.rpc("fundrawtransaction", [txn, options])["hex"]
|
||||
|
||||
def createRawSignedTransaction(self, addr_to, amount) -> str:
|
||||
txn_funded = self.createRawFundedTransaction(addr_to, amount)
|
||||
return self.rpc('signrawtransaction', [txn_funded])['hex']
|
||||
return self.rpc("signrawtransaction", [txn_funded])["hex"]
|
||||
|
||||
def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
|
||||
# Return P2PKH
|
||||
|
@ -188,60 +228,75 @@ class FIROInterface(BTCInterface):
|
|||
return CScript([OP_HASH160, script_hash, OP_EQUAL])
|
||||
|
||||
def withdrawCoin(self, value, addr_to, subfee):
|
||||
params = [addr_to, value, '', '', subfee]
|
||||
return self.rpc('sendtoaddress', params)
|
||||
params = [addr_to, value, "", "", subfee]
|
||||
return self.rpc("sendtoaddress", params)
|
||||
|
||||
def getWalletSeedID(self):
|
||||
return self.rpc('getwalletinfo')['hdmasterkeyid']
|
||||
return self.rpc("getwalletinfo")["hdmasterkeyid"]
|
||||
|
||||
def getSpendableBalance(self) -> int:
|
||||
return self.make_int(self.rpc('getwalletinfo')['balance'])
|
||||
return self.make_int(self.rpc("getwalletinfo")["balance"])
|
||||
|
||||
def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
|
||||
add_bytes = 107
|
||||
size = len(tx.serialize_with_witness()) + add_bytes
|
||||
pay_fee = round(fee_rate * size / 1000)
|
||||
self._log.info(f'BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
|
||||
self._log.info(
|
||||
f"BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}."
|
||||
)
|
||||
return pay_fee
|
||||
|
||||
def signTxWithKey(self, tx: bytes, key: bytes) -> bytes:
|
||||
key_wif = self.encodeKey(key)
|
||||
rv = self.rpc('signrawtransaction', [tx.hex(), [], [key_wif, ]])
|
||||
return bytes.fromhex(rv['hex'])
|
||||
rv = self.rpc(
|
||||
"signrawtransaction",
|
||||
[
|
||||
tx.hex(),
|
||||
[],
|
||||
[
|
||||
key_wif,
|
||||
],
|
||||
],
|
||||
)
|
||||
return bytes.fromhex(rv["hex"])
|
||||
|
||||
def findTxnByHash(self, txid_hex: str):
|
||||
# Only works for wallet txns
|
||||
try:
|
||||
rv = self.rpc('gettransaction', [txid_hex])
|
||||
except Exception as ex:
|
||||
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
|
||||
rv = self.rpc("gettransaction", [txid_hex])
|
||||
except Exception as e: # noqa: F841
|
||||
self._log.debug(
|
||||
"findTxnByHash getrawtransaction failed: {}".format(txid_hex)
|
||||
)
|
||||
return None
|
||||
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
|
||||
block_height = self.getBlockHeader(rv['blockhash'])['height']
|
||||
return {'txid': txid_hex, 'amount': 0, 'height': block_height}
|
||||
if "confirmations" in rv and rv["confirmations"] >= self.blocks_confirmed:
|
||||
block_height = self.getBlockHeader(rv["blockhash"])["height"]
|
||||
return {"txid": txid_hex, "amount": 0, "height": block_height}
|
||||
return None
|
||||
|
||||
def getProofOfFunds(self, amount_for, extra_commit_bytes):
|
||||
# TODO: Lock unspent and use same output/s to fund bid
|
||||
|
||||
unspents_by_addr = dict()
|
||||
unspents = self.rpc('listunspent')
|
||||
unspents = self.rpc("listunspent")
|
||||
for u in unspents:
|
||||
if u['spendable'] is not True:
|
||||
if u["spendable"] is not True:
|
||||
continue
|
||||
if u['address'] not in unspents_by_addr:
|
||||
unspents_by_addr[u['address']] = {'total': 0, 'utxos': []}
|
||||
utxo_amount: int = self.make_int(u['amount'], r=1)
|
||||
unspents_by_addr[u['address']]['total'] += utxo_amount
|
||||
unspents_by_addr[u['address']]['utxos'].append((utxo_amount, u['txid'], u['vout']))
|
||||
if u["address"] not in unspents_by_addr:
|
||||
unspents_by_addr[u["address"]] = {"total": 0, "utxos": []}
|
||||
utxo_amount: int = self.make_int(u["amount"], r=1)
|
||||
unspents_by_addr[u["address"]]["total"] += utxo_amount
|
||||
unspents_by_addr[u["address"]]["utxos"].append(
|
||||
(utxo_amount, u["txid"], u["vout"])
|
||||
)
|
||||
|
||||
max_utxos: int = 4
|
||||
|
||||
viable_addrs = []
|
||||
for addr, data in unspents_by_addr.items():
|
||||
if data['total'] >= amount_for:
|
||||
if data["total"] >= amount_for:
|
||||
# Sort from largest to smallest amount
|
||||
sorted_utxos = sorted(data['utxos'], key=lambda x: x[0])
|
||||
sorted_utxos = sorted(data["utxos"], key=lambda x: x[0])
|
||||
|
||||
# Max outputs required to reach amount_for
|
||||
utxos_req: int = 0
|
||||
|
@ -256,13 +311,17 @@ class FIROInterface(BTCInterface):
|
|||
viable_addrs.append(addr)
|
||||
continue
|
||||
|
||||
ensure(len(viable_addrs) > 0, 'Could not find address with enough funds for proof')
|
||||
ensure(
|
||||
len(viable_addrs) > 0, "Could not find address with enough funds for proof"
|
||||
)
|
||||
|
||||
sign_for_addr: str = random.choice(viable_addrs)
|
||||
self._log.debug('sign_for_addr %s', sign_for_addr)
|
||||
self._log.debug("sign_for_addr %s", sign_for_addr)
|
||||
|
||||
prove_utxos = []
|
||||
sorted_utxos = sorted(unspents_by_addr[sign_for_addr]['utxos'], key=lambda x: x[0])
|
||||
sorted_utxos = sorted(
|
||||
unspents_by_addr[sign_for_addr]["utxos"], key=lambda x: x[0]
|
||||
)
|
||||
|
||||
hasher = hashlib.sha256()
|
||||
|
||||
|
@ -272,18 +331,29 @@ class FIROInterface(BTCInterface):
|
|||
outpoint = (bytes.fromhex(utxo[1]), utxo[2])
|
||||
prove_utxos.append(outpoint)
|
||||
hasher.update(outpoint[0])
|
||||
hasher.update(outpoint[1].to_bytes(2, 'big'))
|
||||
hasher.update(outpoint[1].to_bytes(2, "big"))
|
||||
if sum_value >= amount_for:
|
||||
break
|
||||
utxos_hash = hasher.digest()
|
||||
|
||||
if self.using_segwit(): # TODO: Use isSegwitAddress when scantxoutset can use combo
|
||||
if (
|
||||
self.using_segwit()
|
||||
): # TODO: Use isSegwitAddress when scantxoutset can use combo
|
||||
# 'Address does not refer to key' for non p2pkh
|
||||
pkh = self.decodeAddress(sign_for_addr)
|
||||
sign_for_addr = self.pkh_to_address(pkh)
|
||||
self._log.debug('sign_for_addr converted %s', sign_for_addr)
|
||||
self._log.debug("sign_for_addr converted %s", sign_for_addr)
|
||||
|
||||
signature = self.rpc('signmessage', [sign_for_addr, sign_for_addr + '_swap_proof_' + utxos_hash.hex() + extra_commit_bytes.hex()])
|
||||
signature = self.rpc(
|
||||
"signmessage",
|
||||
[
|
||||
sign_for_addr,
|
||||
sign_for_addr
|
||||
+ "_swap_proof_"
|
||||
+ utxos_hash.hex()
|
||||
+ extra_commit_bytes.hex(),
|
||||
],
|
||||
)
|
||||
|
||||
return (sign_for_addr, signature, prove_utxos)
|
||||
|
||||
|
@ -292,19 +362,23 @@ class FIROInterface(BTCInterface):
|
|||
sum_value: int = 0
|
||||
for outpoint in utxos:
|
||||
hasher.update(outpoint[0])
|
||||
hasher.update(outpoint[1].to_bytes(2, 'big'))
|
||||
hasher.update(outpoint[1].to_bytes(2, "big"))
|
||||
utxos_hash = hasher.digest()
|
||||
|
||||
passed = self.verifyMessage(address, address + '_swap_proof_' + utxos_hash.hex() + extra_commit_bytes.hex(), signature)
|
||||
ensure(passed is True, 'Proof of funds signature invalid')
|
||||
passed = self.verifyMessage(
|
||||
address,
|
||||
address + "_swap_proof_" + utxos_hash.hex() + extra_commit_bytes.hex(),
|
||||
signature,
|
||||
)
|
||||
ensure(passed is True, "Proof of funds signature invalid")
|
||||
|
||||
if self.using_segwit():
|
||||
address = self.encodeSegwitAddress(decodeAddress(address)[1:])
|
||||
|
||||
sum_value: int = 0
|
||||
for outpoint in utxos:
|
||||
txout = self.rpc('gettxout', [outpoint[0].hex(), outpoint[1]])
|
||||
sum_value += self.make_int(txout['value'])
|
||||
txout = self.rpc("gettxout", [outpoint[0].hex(), outpoint[1]])
|
||||
sum_value += self.make_int(txout["value"])
|
||||
|
||||
return sum_value
|
||||
|
||||
|
@ -314,15 +388,15 @@ class FIROInterface(BTCInterface):
|
|||
chain_blocks: int = self.getChainHeight()
|
||||
|
||||
current_height: int = chain_blocks
|
||||
block_hash = self.rpc('getblockhash', [current_height])
|
||||
block_hash = self.rpc("getblockhash", [current_height])
|
||||
|
||||
script_hash: bytes = self.decodeAddress(addr_find)
|
||||
find_scriptPubKey = self.getDestForScriptHash(script_hash)
|
||||
|
||||
while current_height > height_start:
|
||||
block_hash = self.rpc('getblockhash', [current_height])
|
||||
block_hash = self.rpc("getblockhash", [current_height])
|
||||
|
||||
block = self.rpc('getblock', [block_hash, False])
|
||||
block = self.rpc("getblock", [block_hash, False])
|
||||
decoded_block = CBlock()
|
||||
decoded_block = FromHex(decoded_block, block)
|
||||
for tx in decoded_block.vtx:
|
||||
|
@ -330,38 +404,46 @@ class FIROInterface(BTCInterface):
|
|||
if txo.scriptPubKey == find_scriptPubKey:
|
||||
tx.rehash()
|
||||
txid = i2b(tx.sha256)
|
||||
self._log.info('Found output to addr: {} in tx {} in block {}'.format(addr_find, txid.hex(), block_hash))
|
||||
self._log.info('rescanblockchain hack invalidateblock {}'.format(block_hash))
|
||||
self.rpc('invalidateblock', [block_hash])
|
||||
self.rpc('reconsiderblock', [block_hash])
|
||||
self._log.info(
|
||||
"Found output to addr: {} in tx {} in block {}".format(
|
||||
addr_find, txid.hex(), block_hash
|
||||
)
|
||||
)
|
||||
self._log.info(
|
||||
"rescanblockchain hack invalidateblock {}".format(
|
||||
block_hash
|
||||
)
|
||||
)
|
||||
self.rpc("invalidateblock", [block_hash])
|
||||
self.rpc("reconsiderblock", [block_hash])
|
||||
return
|
||||
current_height -= 1
|
||||
|
||||
def getBlockWithTxns(self, block_hash: str):
|
||||
# TODO: Bypass decoderawtransaction and getblockheader
|
||||
block = self.rpc('getblock', [block_hash, False])
|
||||
block_header = self.rpc('getblockheader', [block_hash])
|
||||
block = self.rpc("getblock", [block_hash, False])
|
||||
block_header = self.rpc("getblockheader", [block_hash])
|
||||
decoded_block = CBlock()
|
||||
decoded_block = FromHex(decoded_block, block)
|
||||
|
||||
tx_rv = []
|
||||
for tx in decoded_block.vtx:
|
||||
tx_hex = tx.serialize_with_witness().hex()
|
||||
tx_dec = self.rpc('decoderawtransaction', [tx_hex])
|
||||
if 'hex' not in tx_dec:
|
||||
tx_dec['hex'] = tx_hex
|
||||
tx_dec = self.rpc("decoderawtransaction", [tx_hex])
|
||||
if "hex" not in tx_dec:
|
||||
tx_dec["hex"] = tx_hex
|
||||
|
||||
tx_rv.append(tx_dec)
|
||||
|
||||
block_rv = {
|
||||
'hash': block_hash,
|
||||
'previousblockhash': block_header['previousblockhash'],
|
||||
'tx': tx_rv,
|
||||
'confirmations': block_header['confirmations'],
|
||||
'height': block_header['height'],
|
||||
'time': block_header['time'],
|
||||
'version': block_header['version'],
|
||||
'merkleroot': block_header['merkleroot'],
|
||||
"hash": block_hash,
|
||||
"previousblockhash": block_header["previousblockhash"],
|
||||
"tx": tx_rv,
|
||||
"confirmations": block_header["confirmations"],
|
||||
"height": block_header["height"],
|
||||
"time": block_header["time"],
|
||||
"version": block_header["version"],
|
||||
"merkleroot": block_header["merkleroot"],
|
||||
}
|
||||
|
||||
return block_rv
|
||||
|
|
|
@ -17,63 +17,73 @@ class LTCInterface(BTCInterface):
|
|||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(LTCInterface, self).__init__(coin_settings, network, swap_client)
|
||||
self._rpc_wallet_mweb = 'mweb'
|
||||
self.rpc_wallet_mweb = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host, wallet=self._rpc_wallet_mweb)
|
||||
self._rpc_wallet_mweb = "mweb"
|
||||
self.rpc_wallet_mweb = make_rpc_func(
|
||||
self._rpcport,
|
||||
self._rpcauth,
|
||||
host=self._rpc_host,
|
||||
wallet=self._rpc_wallet_mweb,
|
||||
)
|
||||
|
||||
def getNewMwebAddress(self, use_segwit=False, label='swap_receive') -> str:
|
||||
return self.rpc_wallet_mweb('getnewaddress', [label, 'mweb'])
|
||||
def getNewMwebAddress(self, use_segwit=False, label="swap_receive") -> str:
|
||||
return self.rpc_wallet_mweb("getnewaddress", [label, "mweb"])
|
||||
|
||||
def getNewStealthAddress(self, label=''):
|
||||
def getNewStealthAddress(self, label=""):
|
||||
return self.getNewMwebAddress(False, label)
|
||||
|
||||
def withdrawCoin(self, value, type_from: str, addr_to: str, subfee: bool) -> str:
|
||||
params = [addr_to, value, '', '', subfee, True, self._conf_target]
|
||||
if type_from == 'mweb':
|
||||
return self.rpc_wallet_mweb('sendtoaddress', params)
|
||||
return self.rpc_wallet('sendtoaddress', params)
|
||||
params = [addr_to, value, "", "", subfee, True, self._conf_target]
|
||||
if type_from == "mweb":
|
||||
return self.rpc_wallet_mweb("sendtoaddress", params)
|
||||
return self.rpc_wallet("sendtoaddress", params)
|
||||
|
||||
def createUTXO(self, value_sats: int):
|
||||
# Create a new address and send value_sats to it
|
||||
|
||||
spendable_balance = self.getSpendableBalance()
|
||||
if spendable_balance < value_sats:
|
||||
raise ValueError('Balance too low')
|
||||
raise ValueError("Balance too low")
|
||||
|
||||
address = self.getNewAddress(self._use_segwit, 'create_utxo')
|
||||
return self.withdrawCoin(self.format_amount(value_sats), 'plain', address, False), address
|
||||
address = self.getNewAddress(self._use_segwit, "create_utxo")
|
||||
return (
|
||||
self.withdrawCoin(self.format_amount(value_sats), "plain", address, False),
|
||||
address,
|
||||
)
|
||||
|
||||
def getWalletInfo(self):
|
||||
rv = super(LTCInterface, self).getWalletInfo()
|
||||
|
||||
mweb_info = self.rpc_wallet_mweb('getwalletinfo')
|
||||
rv['mweb_balance'] = mweb_info['balance']
|
||||
rv['mweb_unconfirmed'] = mweb_info['unconfirmed_balance']
|
||||
rv['mweb_immature'] = mweb_info['immature_balance']
|
||||
mweb_info = self.rpc_wallet_mweb("getwalletinfo")
|
||||
rv["mweb_balance"] = mweb_info["balance"]
|
||||
rv["mweb_unconfirmed"] = mweb_info["unconfirmed_balance"]
|
||||
rv["mweb_immature"] = mweb_info["immature_balance"]
|
||||
return rv
|
||||
|
||||
def getUnspentsByAddr(self):
|
||||
unspent_addr = dict()
|
||||
unspent = self.rpc_wallet('listunspent')
|
||||
unspent = self.rpc_wallet("listunspent")
|
||||
for u in unspent:
|
||||
if u.get('spendable', False) is False:
|
||||
if u.get("spendable", False) is False:
|
||||
continue
|
||||
if u.get('solvable', False) is False: # Filter out mweb outputs
|
||||
if u.get("solvable", False) is False: # Filter out mweb outputs
|
||||
continue
|
||||
if 'address' not in u:
|
||||
if "address" not in u:
|
||||
continue
|
||||
if 'desc' in u:
|
||||
desc = u['desc']
|
||||
if "desc" in u:
|
||||
desc = u["desc"]
|
||||
if self.using_segwit:
|
||||
if self.use_p2shp2wsh():
|
||||
if not desc.startswith('sh(wpkh'):
|
||||
if not desc.startswith("sh(wpkh"):
|
||||
continue
|
||||
else:
|
||||
if not desc.startswith('wpkh'):
|
||||
if not desc.startswith("wpkh"):
|
||||
continue
|
||||
else:
|
||||
if not desc.startswith('pkh'):
|
||||
if not desc.startswith("pkh"):
|
||||
continue
|
||||
unspent_addr[u['address']] = unspent_addr.get(u['address'], 0) + self.make_int(u['amount'], r=1)
|
||||
unspent_addr[u["address"]] = unspent_addr.get(
|
||||
u["address"], 0
|
||||
) + self.make_int(u["amount"], r=1)
|
||||
return unspent_addr
|
||||
|
||||
|
||||
|
@ -84,8 +94,10 @@ class LTCInterfaceMWEB(LTCInterface):
|
|||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(LTCInterfaceMWEB, self).__init__(coin_settings, network, swap_client)
|
||||
self._rpc_wallet = 'mweb'
|
||||
self.rpc_wallet = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host, wallet=self._rpc_wallet)
|
||||
self._rpc_wallet = "mweb"
|
||||
self.rpc_wallet = make_rpc_func(
|
||||
self._rpcport, self._rpcauth, host=self._rpc_host, wallet=self._rpc_wallet
|
||||
)
|
||||
|
||||
def chainparams(self):
|
||||
return chainparams[Coins.LTC]
|
||||
|
@ -95,54 +107,54 @@ class LTCInterfaceMWEB(LTCInterface):
|
|||
|
||||
def coin_name(self) -> str:
|
||||
coin_chainparams = chainparams[Coins.LTC]
|
||||
return coin_chainparams['name'].capitalize() + ' MWEB'
|
||||
return coin_chainparams["name"].capitalize() + " MWEB"
|
||||
|
||||
def ticker(self) -> str:
|
||||
ticker = chainparams[Coins.LTC]['ticker']
|
||||
if self._network == 'testnet':
|
||||
ticker = 't' + ticker
|
||||
elif self._network == 'regtest':
|
||||
ticker = 'rt' + ticker
|
||||
return ticker + '_MWEB'
|
||||
ticker = chainparams[Coins.LTC]["ticker"]
|
||||
if self._network == "testnet":
|
||||
ticker = "t" + ticker
|
||||
elif self._network == "regtest":
|
||||
ticker = "rt" + ticker
|
||||
return ticker + "_MWEB"
|
||||
|
||||
def getNewAddress(self, use_segwit=False, label='swap_receive') -> str:
|
||||
def getNewAddress(self, use_segwit=False, label="swap_receive") -> str:
|
||||
return self.getNewMwebAddress()
|
||||
|
||||
def has_mweb_wallet(self) -> bool:
|
||||
return 'mweb' in self.rpc('listwallets')
|
||||
return "mweb" in self.rpc("listwallets")
|
||||
|
||||
def init_wallet(self, password=None):
|
||||
# If system is encrypted mweb wallet will be created at first unlock
|
||||
|
||||
self._log.info('init_wallet - {}'.format(self.ticker()))
|
||||
self._log.info("init_wallet - {}".format(self.ticker()))
|
||||
|
||||
self._log.info('Creating mweb wallet for {}.'.format(self.coin_name()))
|
||||
self._log.info("Creating mweb wallet for {}.".format(self.coin_name()))
|
||||
# wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors, load_on_startup
|
||||
self.rpc('createwallet', ['mweb', False, True, password, False, False, True])
|
||||
self.rpc("createwallet", ["mweb", False, True, password, False, False, True])
|
||||
|
||||
if password is not None:
|
||||
# Max timeout value, ~3 years
|
||||
self.rpc_wallet('walletpassphrase', [password, 100000000])
|
||||
self.rpc_wallet("walletpassphrase", [password, 100000000])
|
||||
|
||||
if self.getWalletSeedID() == 'Not found':
|
||||
if self.getWalletSeedID() == "Not found":
|
||||
self._sc.initialiseWallet(self.coin_type())
|
||||
|
||||
# Workaround to trigger mweb_spk_man->LoadMWEBKeychain()
|
||||
self.rpc('unloadwallet', ['mweb'])
|
||||
self.rpc('loadwallet', ['mweb'])
|
||||
self.rpc("unloadwallet", ["mweb"])
|
||||
self.rpc("loadwallet", ["mweb"])
|
||||
if password is not None:
|
||||
self.rpc_wallet('walletpassphrase', [password, 100000000])
|
||||
self.rpc_wallet('keypoolrefill')
|
||||
self.rpc_wallet("walletpassphrase", [password, 100000000])
|
||||
self.rpc_wallet("keypoolrefill")
|
||||
|
||||
def unlockWallet(self, password: str):
|
||||
if password == '':
|
||||
if password == "":
|
||||
return
|
||||
self._log.info('unlockWallet - {}'.format(self.ticker()))
|
||||
self._log.info("unlockWallet - {}".format(self.ticker()))
|
||||
|
||||
if not self.has_mweb_wallet():
|
||||
self.init_wallet(password)
|
||||
else:
|
||||
# Max timeout value, ~3 years
|
||||
self.rpc_wallet('walletpassphrase', [password, 100000000])
|
||||
self.rpc_wallet("walletpassphrase", [password, 100000000])
|
||||
|
||||
self._sc.checkWalletSeed(self.coin_type())
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2023 tecnovert
|
||||
# Copyright (c) 2024 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -38,7 +39,9 @@ from basicswap.util.address import (
|
|||
encodeAddress,
|
||||
)
|
||||
from basicswap.util import (
|
||||
b2i, i2b, i2h,
|
||||
b2i,
|
||||
i2b,
|
||||
i2h,
|
||||
ensure,
|
||||
)
|
||||
from basicswap.basicswap_util import (
|
||||
|
@ -49,7 +52,10 @@ from basicswap.interface.contrib.nav_test_framework.script import (
|
|||
CScript,
|
||||
OP_0,
|
||||
OP_EQUAL,
|
||||
OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG,
|
||||
OP_DUP,
|
||||
OP_HASH160,
|
||||
OP_EQUALVERIFY,
|
||||
OP_CHECKSIG,
|
||||
SIGHASH_ALL,
|
||||
SegwitVersion1SignatureHash,
|
||||
)
|
||||
|
@ -71,7 +77,9 @@ class NAVInterface(BTCInterface):
|
|||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(NAVInterface, self).__init__(coin_settings, network, swap_client)
|
||||
# No multiwallet support
|
||||
self.rpc_wallet = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host)
|
||||
self.rpc_wallet = make_rpc_func(
|
||||
self._rpcport, self._rpcauth, host=self._rpc_host
|
||||
)
|
||||
|
||||
def use_p2shp2wsh(self) -> bool:
|
||||
# p2sh-p2wsh
|
||||
|
@ -85,31 +93,31 @@ class NAVInterface(BTCInterface):
|
|||
return 1
|
||||
|
||||
def getWalletSeedID(self):
|
||||
return self.rpc('getwalletinfo')['hdmasterkeyid']
|
||||
return self.rpc("getwalletinfo")["hdmasterkeyid"]
|
||||
|
||||
def withdrawCoin(self, value, addr_to: str, subfee: bool):
|
||||
strdzeel = ''
|
||||
params = [addr_to, value, '', '', strdzeel, subfee]
|
||||
return self.rpc('sendtoaddress', params)
|
||||
strdzeel = ""
|
||||
params = [addr_to, value, "", "", strdzeel, subfee]
|
||||
return self.rpc("sendtoaddress", params)
|
||||
|
||||
def getSpendableBalance(self) -> int:
|
||||
return self.make_int(self.rpc('getwalletinfo')['balance'])
|
||||
return self.make_int(self.rpc("getwalletinfo")["balance"])
|
||||
|
||||
def signTxWithWallet(self, tx: bytes) -> bytes:
|
||||
rv = self.rpc('signrawtransaction', [tx.hex()])
|
||||
rv = self.rpc("signrawtransaction", [tx.hex()])
|
||||
|
||||
return bytes.fromhex(rv['hex'])
|
||||
return bytes.fromhex(rv["hex"])
|
||||
|
||||
def checkExpectedSeed(self, key_hash: str):
|
||||
try:
|
||||
rv = self.rpc('dumpmnemonic')
|
||||
entropy = Mnemonic('english').to_entropy(rv.split(' '))
|
||||
rv = self.rpc("dumpmnemonic")
|
||||
entropy = Mnemonic("english").to_entropy(rv.split(" "))
|
||||
|
||||
entropy_hash = self.getAddressHashFromKey(entropy)[::-1].hex()
|
||||
self._have_checked_seed = True
|
||||
return entropy_hash == key_hash
|
||||
except Exception as e:
|
||||
self._log.warning('checkExpectedSeed failed: {}'.format(str(e)))
|
||||
self._log.warning("checkExpectedSeed failed: {}".format(str(e)))
|
||||
return False
|
||||
|
||||
def getScriptForP2PKH(self, pkh: bytes) -> bytearray:
|
||||
|
@ -134,13 +142,22 @@ class NAVInterface(BTCInterface):
|
|||
script = CScript([OP_0, pkh])
|
||||
script_hash = hash160(script)
|
||||
assert len(script_hash) == 20
|
||||
return encodeAddress(bytes((self.chainparams_network()['script_address'],)) + script_hash)
|
||||
return encodeAddress(
|
||||
bytes((self.chainparams_network()["script_address"],)) + script_hash
|
||||
)
|
||||
|
||||
def encodeSegwitAddressScript(self, script: bytes) -> str:
|
||||
if len(script) == 23 and script[0] == OP_HASH160 and script[1] == 20 and script[22] == OP_EQUAL:
|
||||
if (
|
||||
len(script) == 23
|
||||
and script[0] == OP_HASH160
|
||||
and script[1] == 20
|
||||
and script[22] == OP_EQUAL
|
||||
):
|
||||
script_hash = script[2:22]
|
||||
return encodeAddress(bytes((self.chainparams_network()['script_address'],)) + script_hash)
|
||||
raise ValueError('Unknown Script')
|
||||
return encodeAddress(
|
||||
bytes((self.chainparams_network()["script_address"],)) + script_hash
|
||||
)
|
||||
raise ValueError("Unknown Script")
|
||||
|
||||
def loadTx(self, tx_bytes: bytes) -> CTransaction:
|
||||
# Load tx from bytes to internal representation
|
||||
|
@ -148,9 +165,18 @@ class NAVInterface(BTCInterface):
|
|||
tx.deserialize(BytesIO(tx_bytes))
|
||||
return tx
|
||||
|
||||
def signTx(self, key_bytes: bytes, tx_bytes: bytes, input_n: int, prevout_script, prevout_value: int):
|
||||
def signTx(
|
||||
self,
|
||||
key_bytes: bytes,
|
||||
tx_bytes: bytes,
|
||||
input_n: int,
|
||||
prevout_script,
|
||||
prevout_value: int,
|
||||
):
|
||||
tx = self.loadTx(tx_bytes)
|
||||
sig_hash = SegwitVersion1SignatureHash(prevout_script, tx, input_n, SIGHASH_ALL, prevout_value)
|
||||
sig_hash = SegwitVersion1SignatureHash(
|
||||
prevout_script, tx, input_n, SIGHASH_ALL, prevout_value
|
||||
)
|
||||
eck = PrivateKey(key_bytes)
|
||||
return eck.sign(sig_hash, hasher=None) + bytes((SIGHASH_ALL,))
|
||||
|
||||
|
@ -165,23 +191,25 @@ class NAVInterface(BTCInterface):
|
|||
# TODO: Lock unspent and use same output/s to fund bid
|
||||
|
||||
unspents_by_addr = dict()
|
||||
unspents = self.rpc('listunspent')
|
||||
unspents = self.rpc("listunspent")
|
||||
for u in unspents:
|
||||
if u['spendable'] is not True:
|
||||
if u["spendable"] is not True:
|
||||
continue
|
||||
if u['address'] not in unspents_by_addr:
|
||||
unspents_by_addr[u['address']] = {'total': 0, 'utxos': []}
|
||||
utxo_amount: int = self.make_int(u['amount'], r=1)
|
||||
unspents_by_addr[u['address']]['total'] += utxo_amount
|
||||
unspents_by_addr[u['address']]['utxos'].append((utxo_amount, u['txid'], u['vout']))
|
||||
if u["address"] not in unspents_by_addr:
|
||||
unspents_by_addr[u["address"]] = {"total": 0, "utxos": []}
|
||||
utxo_amount: int = self.make_int(u["amount"], r=1)
|
||||
unspents_by_addr[u["address"]]["total"] += utxo_amount
|
||||
unspents_by_addr[u["address"]]["utxos"].append(
|
||||
(utxo_amount, u["txid"], u["vout"])
|
||||
)
|
||||
|
||||
max_utxos: int = 4
|
||||
|
||||
viable_addrs = []
|
||||
for addr, data in unspents_by_addr.items():
|
||||
if data['total'] >= amount_for:
|
||||
if data["total"] >= amount_for:
|
||||
# Sort from largest to smallest amount
|
||||
sorted_utxos = sorted(data['utxos'], key=lambda x: x[0])
|
||||
sorted_utxos = sorted(data["utxos"], key=lambda x: x[0])
|
||||
|
||||
# Max outputs required to reach amount_for
|
||||
utxos_req: int = 0
|
||||
|
@ -196,13 +224,17 @@ class NAVInterface(BTCInterface):
|
|||
viable_addrs.append(addr)
|
||||
continue
|
||||
|
||||
ensure(len(viable_addrs) > 0, 'Could not find address with enough funds for proof')
|
||||
ensure(
|
||||
len(viable_addrs) > 0, "Could not find address with enough funds for proof"
|
||||
)
|
||||
|
||||
sign_for_addr: str = random.choice(viable_addrs)
|
||||
self._log.debug('sign_for_addr %s', sign_for_addr)
|
||||
self._log.debug("sign_for_addr %s", sign_for_addr)
|
||||
|
||||
prove_utxos = []
|
||||
sorted_utxos = sorted(unspents_by_addr[sign_for_addr]['utxos'], key=lambda x: x[0])
|
||||
sorted_utxos = sorted(
|
||||
unspents_by_addr[sign_for_addr]["utxos"], key=lambda x: x[0]
|
||||
)
|
||||
|
||||
hasher = hashlib.sha256()
|
||||
|
||||
|
@ -212,20 +244,36 @@ class NAVInterface(BTCInterface):
|
|||
outpoint = (bytes.fromhex(utxo[1]), utxo[2])
|
||||
prove_utxos.append(outpoint)
|
||||
hasher.update(outpoint[0])
|
||||
hasher.update(outpoint[1].to_bytes(2, 'big'))
|
||||
hasher.update(outpoint[1].to_bytes(2, "big"))
|
||||
if sum_value >= amount_for:
|
||||
break
|
||||
utxos_hash = hasher.digest()
|
||||
|
||||
if self.using_segwit(): # TODO: Use isSegwitAddress when scantxoutset can use combo
|
||||
if (
|
||||
self.using_segwit()
|
||||
): # TODO: Use isSegwitAddress when scantxoutset can use combo
|
||||
# 'Address does not refer to key' for non p2pkh
|
||||
addr_info = self.rpc('validateaddress', [addr, ])
|
||||
if 'isscript' in addr_info and addr_info['isscript'] and 'hex' in addr_info:
|
||||
pkh = bytes.fromhex(addr_info['hex'])[2:]
|
||||
addr_info = self.rpc(
|
||||
"validateaddress",
|
||||
[
|
||||
addr,
|
||||
],
|
||||
)
|
||||
if "isscript" in addr_info and addr_info["isscript"] and "hex" in addr_info:
|
||||
pkh = bytes.fromhex(addr_info["hex"])[2:]
|
||||
sign_for_addr = self.pkh_to_address(pkh)
|
||||
self._log.debug('sign_for_addr converted %s', sign_for_addr)
|
||||
self._log.debug("sign_for_addr converted %s", sign_for_addr)
|
||||
|
||||
signature = self.rpc('signmessage', [sign_for_addr, sign_for_addr + '_swap_proof_' + utxos_hash.hex() + extra_commit_bytes.hex()])
|
||||
signature = self.rpc(
|
||||
"signmessage",
|
||||
[
|
||||
sign_for_addr,
|
||||
sign_for_addr
|
||||
+ "_swap_proof_"
|
||||
+ utxos_hash.hex()
|
||||
+ extra_commit_bytes.hex(),
|
||||
],
|
||||
)
|
||||
|
||||
return (sign_for_addr, signature, prove_utxos)
|
||||
|
||||
|
@ -234,48 +282,64 @@ class NAVInterface(BTCInterface):
|
|||
sum_value: int = 0
|
||||
for outpoint in utxos:
|
||||
hasher.update(outpoint[0])
|
||||
hasher.update(outpoint[1].to_bytes(2, 'big'))
|
||||
hasher.update(outpoint[1].to_bytes(2, "big"))
|
||||
utxos_hash = hasher.digest()
|
||||
|
||||
passed = self.verifyMessage(address, address + '_swap_proof_' + utxos_hash.hex() + extra_commit_bytes.hex(), signature)
|
||||
ensure(passed is True, 'Proof of funds signature invalid')
|
||||
passed = self.verifyMessage(
|
||||
address,
|
||||
address + "_swap_proof_" + utxos_hash.hex() + extra_commit_bytes.hex(),
|
||||
signature,
|
||||
)
|
||||
ensure(passed is True, "Proof of funds signature invalid")
|
||||
|
||||
if self.using_segwit():
|
||||
address = self.encodeSegwitAddress(self.decodeAddress(address)[1:])
|
||||
|
||||
sum_value: int = 0
|
||||
for outpoint in utxos:
|
||||
txout = self.rpc('gettxout', [outpoint[0].hex(), outpoint[1]])
|
||||
sum_value += self.make_int(txout['value'])
|
||||
txout = self.rpc("gettxout", [outpoint[0].hex(), outpoint[1]])
|
||||
sum_value += self.make_int(txout["value"])
|
||||
|
||||
return sum_value
|
||||
|
||||
def createRawFundedTransaction(self, addr_to: str, amount: int, sub_fee: bool = False, lock_unspents: bool = True) -> str:
|
||||
txn = self.rpc('createrawtransaction', [[], {addr_to: self.format_amount(amount)}])
|
||||
def createRawFundedTransaction(
|
||||
self,
|
||||
addr_to: str,
|
||||
amount: int,
|
||||
sub_fee: bool = False,
|
||||
lock_unspents: bool = True,
|
||||
) -> str:
|
||||
txn = self.rpc(
|
||||
"createrawtransaction", [[], {addr_to: self.format_amount(amount)}]
|
||||
)
|
||||
fee_rate, fee_src = self.get_fee_rate(self._conf_target)
|
||||
self._log.debug(f'Fee rate: {fee_rate}, source: {fee_src}, block target: {self._conf_target}')
|
||||
self._log.debug(
|
||||
f"Fee rate: {fee_rate}, source: {fee_src}, block target: {self._conf_target}"
|
||||
)
|
||||
if sub_fee:
|
||||
raise ValueError('Navcoin fundrawtransaction is missing the subtractFeeFromOutputs parameter')
|
||||
raise ValueError(
|
||||
"Navcoin fundrawtransaction is missing the subtractFeeFromOutputs parameter"
|
||||
)
|
||||
# options['subtractFeeFromOutputs'] = [0,]
|
||||
|
||||
fee_rate = self.make_int(fee_rate, r=1)
|
||||
return self.fundTx(txn, fee_rate, lock_unspents).hex()
|
||||
|
||||
def isAddressMine(self, address: str, or_watch_only: bool = False) -> bool:
|
||||
addr_info = self.rpc('validateaddress', [address])
|
||||
addr_info = self.rpc("validateaddress", [address])
|
||||
if not or_watch_only:
|
||||
return addr_info['ismine']
|
||||
return addr_info['ismine'] or addr_info['iswatchonly']
|
||||
return addr_info["ismine"]
|
||||
return addr_info["ismine"] or addr_info["iswatchonly"]
|
||||
|
||||
def createRawSignedTransaction(self, addr_to, amount) -> str:
|
||||
txn_funded = self.createRawFundedTransaction(addr_to, amount)
|
||||
return self.rpc('signrawtransaction', [txn_funded])['hex']
|
||||
return self.rpc("signrawtransaction", [txn_funded])["hex"]
|
||||
|
||||
def getBlockchainInfo(self):
|
||||
rv = self.rpc('getblockchaininfo')
|
||||
synced = round(rv['verificationprogress'], 3)
|
||||
rv = self.rpc("getblockchaininfo")
|
||||
synced = round(rv["verificationprogress"], 3)
|
||||
if synced >= 0.997:
|
||||
rv['verificationprogress'] = 1.0
|
||||
rv["verificationprogress"] = 1.0
|
||||
return rv
|
||||
|
||||
def encodeScriptDest(self, script_dest: bytes) -> str:
|
||||
|
@ -283,47 +347,75 @@ class NAVInterface(BTCInterface):
|
|||
return self.sh_to_address(script_hash)
|
||||
|
||||
def encode_p2wsh(self, script: bytes) -> str:
|
||||
return pubkeyToAddress(self.chainparams_network()['script_address'], script)
|
||||
return pubkeyToAddress(self.chainparams_network()["script_address"], script)
|
||||
|
||||
def find_prevout_info(self, txn_hex: str, txn_script: bytes):
|
||||
txjs = self.rpc('decoderawtransaction', [txn_hex])
|
||||
txjs = self.rpc("decoderawtransaction", [txn_hex])
|
||||
n = getVoutByScriptPubKey(txjs, self.getScriptDest(txn_script).hex())
|
||||
|
||||
return {
|
||||
'txid': txjs['txid'],
|
||||
'vout': n,
|
||||
'scriptPubKey': txjs['vout'][n]['scriptPubKey']['hex'],
|
||||
'redeemScript': txn_script.hex(),
|
||||
'amount': txjs['vout'][n]['value']
|
||||
"txid": txjs["txid"],
|
||||
"vout": n,
|
||||
"scriptPubKey": txjs["vout"][n]["scriptPubKey"]["hex"],
|
||||
"redeemScript": txn_script.hex(),
|
||||
"amount": txjs["vout"][n]["value"],
|
||||
}
|
||||
|
||||
def getNewAddress(self, use_segwit: bool, label: str = 'swap_receive') -> str:
|
||||
address: str = self.rpc('getnewaddress', [label,])
|
||||
def getNewAddress(self, use_segwit: bool, label: str = "swap_receive") -> str:
|
||||
address: str = self.rpc(
|
||||
"getnewaddress",
|
||||
[
|
||||
label,
|
||||
],
|
||||
)
|
||||
if use_segwit:
|
||||
return self.rpc('addwitnessaddress', [address,])
|
||||
return self.rpc(
|
||||
"addwitnessaddress",
|
||||
[
|
||||
address,
|
||||
],
|
||||
)
|
||||
return address
|
||||
|
||||
def createRedeemTxn(self, prevout, output_addr: str, output_value: int, txn_script: bytes) -> str:
|
||||
def createRedeemTxn(
|
||||
self, prevout, output_addr: str, output_value: int, txn_script: bytes
|
||||
) -> str:
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
prev_txid = b2i(bytes.fromhex(prevout['txid']))
|
||||
prev_txid = b2i(bytes.fromhex(prevout["txid"]))
|
||||
|
||||
tx.vin.append(CTxIn(COutPoint(prev_txid, prevout['vout']),
|
||||
scriptSig=self.getScriptScriptSig(txn_script)))
|
||||
tx.vin.append(
|
||||
CTxIn(
|
||||
COutPoint(prev_txid, prevout["vout"]),
|
||||
scriptSig=self.getScriptScriptSig(txn_script),
|
||||
)
|
||||
)
|
||||
pkh = self.decodeAddress(output_addr)
|
||||
script = self.getScriptForPubkeyHash(pkh)
|
||||
tx.vout.append(self.txoType()(output_value, script))
|
||||
tx.rehash()
|
||||
return tx.serialize().hex()
|
||||
|
||||
def createRefundTxn(self, prevout, output_addr: str, output_value: int, locktime: int, sequence: int, txn_script: bytes) -> str:
|
||||
def createRefundTxn(
|
||||
self,
|
||||
prevout,
|
||||
output_addr: str,
|
||||
output_value: int,
|
||||
locktime: int,
|
||||
sequence: int,
|
||||
txn_script: bytes,
|
||||
) -> str:
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
tx.nLockTime = locktime
|
||||
prev_txid = b2i(bytes.fromhex(prevout['txid']))
|
||||
tx.vin.append(CTxIn(COutPoint(prev_txid, prevout['vout']),
|
||||
nSequence=sequence,
|
||||
scriptSig=self.getScriptScriptSig(txn_script)))
|
||||
prev_txid = b2i(bytes.fromhex(prevout["txid"]))
|
||||
tx.vin.append(
|
||||
CTxIn(
|
||||
COutPoint(prev_txid, prevout["vout"]),
|
||||
nSequence=sequence,
|
||||
scriptSig=self.getScriptScriptSig(txn_script),
|
||||
)
|
||||
)
|
||||
pkh = self.decodeAddress(output_addr)
|
||||
script = self.getScriptForPubkeyHash(pkh)
|
||||
tx.vout.append(self.txoType()(output_value, script))
|
||||
|
@ -332,22 +424,38 @@ class NAVInterface(BTCInterface):
|
|||
|
||||
def getTxSignature(self, tx_hex: str, prevout_data, key_wif: str) -> str:
|
||||
key = decodeWif(key_wif)
|
||||
redeem_script = bytes.fromhex(prevout_data['redeemScript'])
|
||||
sig = self.signTx(key, bytes.fromhex(tx_hex), 0, redeem_script, self.make_int(prevout_data['amount']))
|
||||
redeem_script = bytes.fromhex(prevout_data["redeemScript"])
|
||||
sig = self.signTx(
|
||||
key,
|
||||
bytes.fromhex(tx_hex),
|
||||
0,
|
||||
redeem_script,
|
||||
self.make_int(prevout_data["amount"]),
|
||||
)
|
||||
|
||||
return sig.hex()
|
||||
|
||||
def verifyTxSig(self, tx_bytes: bytes, sig: bytes, K: bytes, input_n: int, prevout_script: bytes, prevout_value: int) -> bool:
|
||||
def verifyTxSig(
|
||||
self,
|
||||
tx_bytes: bytes,
|
||||
sig: bytes,
|
||||
K: bytes,
|
||||
input_n: int,
|
||||
prevout_script: bytes,
|
||||
prevout_value: int,
|
||||
) -> bool:
|
||||
tx = self.loadTx(tx_bytes)
|
||||
sig_hash = SegwitVersion1SignatureHash(prevout_script, tx, input_n, SIGHASH_ALL, prevout_value)
|
||||
sig_hash = SegwitVersion1SignatureHash(
|
||||
prevout_script, tx, input_n, SIGHASH_ALL, prevout_value
|
||||
)
|
||||
|
||||
pubkey = PublicKey(K)
|
||||
return pubkey.verify(sig[: -1], sig_hash, hasher=None) # Pop the hashtype byte
|
||||
return pubkey.verify(sig[:-1], sig_hash, hasher=None) # Pop the hashtype byte
|
||||
|
||||
def verifyRawTransaction(self, tx_hex: str, prevouts):
|
||||
# Only checks signature
|
||||
# verifyrawtransaction
|
||||
self._log.warning('NAV verifyRawTransaction only checks signature')
|
||||
self._log.warning("NAV verifyRawTransaction only checks signature")
|
||||
inputs_valid: bool = False
|
||||
validscripts: int = 0
|
||||
|
||||
|
@ -359,22 +467,26 @@ class NAVInterface(BTCInterface):
|
|||
|
||||
input_n: int = 0
|
||||
prevout_data = prevouts[input_n]
|
||||
redeem_script = bytes.fromhex(prevout_data['redeemScript'])
|
||||
prevout_value = self.make_int(prevout_data['amount'])
|
||||
redeem_script = bytes.fromhex(prevout_data["redeemScript"])
|
||||
prevout_value = self.make_int(prevout_data["amount"])
|
||||
|
||||
if self.verifyTxSig(tx_bytes, signature, pubkey, input_n, redeem_script, prevout_value):
|
||||
if self.verifyTxSig(
|
||||
tx_bytes, signature, pubkey, input_n, redeem_script, prevout_value
|
||||
):
|
||||
validscripts += 1
|
||||
|
||||
# TODO: validate inputs
|
||||
inputs_valid = True
|
||||
|
||||
return {
|
||||
'inputs_valid': inputs_valid,
|
||||
'validscripts': validscripts,
|
||||
"inputs_valid": inputs_valid,
|
||||
"validscripts": validscripts,
|
||||
}
|
||||
|
||||
def getHTLCSpendTxVSize(self, redeem: bool = True) -> int:
|
||||
tx_vsize = 5 # Add a few bytes, sequence in script takes variable amount of bytes
|
||||
tx_vsize = (
|
||||
5 # Add a few bytes, sequence in script takes variable amount of bytes
|
||||
)
|
||||
|
||||
tx_vsize += 184 if redeem else 187
|
||||
return tx_vsize
|
||||
|
@ -393,15 +505,15 @@ class NAVInterface(BTCInterface):
|
|||
chain_blocks: int = self.getChainHeight()
|
||||
|
||||
current_height: int = chain_blocks
|
||||
block_hash = self.rpc('getblockhash', [current_height])
|
||||
block_hash = self.rpc("getblockhash", [current_height])
|
||||
|
||||
script_hash: bytes = self.decodeAddress(addr_find)
|
||||
find_scriptPubKey = self.getDestForScriptHash(script_hash)
|
||||
|
||||
while current_height > height_start:
|
||||
block_hash = self.rpc('getblockhash', [current_height])
|
||||
block_hash = self.rpc("getblockhash", [current_height])
|
||||
|
||||
block = self.rpc('getblock', [block_hash, False])
|
||||
block = self.rpc("getblock", [block_hash, False])
|
||||
decoded_block = CBlock()
|
||||
decoded_block = FromHex(decoded_block, block)
|
||||
for tx in decoded_block.vtx:
|
||||
|
@ -409,84 +521,116 @@ class NAVInterface(BTCInterface):
|
|||
if txo.scriptPubKey == find_scriptPubKey:
|
||||
tx.rehash()
|
||||
txid = i2b(tx.sha256)
|
||||
self._log.info('Found output to addr: {} in tx {} in block {}'.format(addr_find, txid.hex(), block_hash))
|
||||
self._log.info('rescanblockchain hack invalidateblock {}'.format(block_hash))
|
||||
self.rpc('invalidateblock', [block_hash])
|
||||
self.rpc('reconsiderblock', [block_hash])
|
||||
self._log.info(
|
||||
"Found output to addr: {} in tx {} in block {}".format(
|
||||
addr_find, txid.hex(), block_hash
|
||||
)
|
||||
)
|
||||
self._log.info(
|
||||
"rescanblockchain hack invalidateblock {}".format(
|
||||
block_hash
|
||||
)
|
||||
)
|
||||
self.rpc("invalidateblock", [block_hash])
|
||||
self.rpc("reconsiderblock", [block_hash])
|
||||
return
|
||||
current_height -= 1
|
||||
|
||||
def getLockTxHeight(self, txid, dest_address, bid_amount, rescan_from, find_index: bool = False, vout: int = -1):
|
||||
def getLockTxHeight(
|
||||
self,
|
||||
txid,
|
||||
dest_address,
|
||||
bid_amount,
|
||||
rescan_from,
|
||||
find_index: bool = False,
|
||||
vout: int = -1,
|
||||
):
|
||||
# Add watchonly address and rescan if required
|
||||
|
||||
if not self.isAddressMine(dest_address, or_watch_only=True):
|
||||
self.importWatchOnlyAddress(dest_address, 'bid')
|
||||
self._log.info('Imported watch-only addr: {}'.format(dest_address))
|
||||
self._log.info('Rescanning {} chain from height: {}'.format(self.coin_name(), rescan_from))
|
||||
self.importWatchOnlyAddress(dest_address, "bid")
|
||||
self._log.info("Imported watch-only addr: {}".format(dest_address))
|
||||
self._log.info(
|
||||
"Rescanning {} chain from height: {}".format(
|
||||
self.coin_name(), rescan_from
|
||||
)
|
||||
)
|
||||
self.rescanBlockchainForAddress(rescan_from, dest_address)
|
||||
|
||||
return_txid = True if txid is None else False
|
||||
if txid is None:
|
||||
txns = self.rpc('listunspent', [0, 9999999, [dest_address, ]])
|
||||
txns = self.rpc(
|
||||
"listunspent",
|
||||
[
|
||||
0,
|
||||
9999999,
|
||||
[
|
||||
dest_address,
|
||||
],
|
||||
],
|
||||
)
|
||||
|
||||
for tx in txns:
|
||||
if self.make_int(tx['amount']) == bid_amount:
|
||||
txid = bytes.fromhex(tx['txid'])
|
||||
if self.make_int(tx["amount"]) == bid_amount:
|
||||
txid = bytes.fromhex(tx["txid"])
|
||||
break
|
||||
|
||||
if txid is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
tx = self.rpc('gettransaction', [txid.hex()])
|
||||
tx = self.rpc("gettransaction", [txid.hex()])
|
||||
|
||||
block_height = 0
|
||||
if 'blockhash' in tx:
|
||||
block_header = self.rpc('getblockheader', [tx['blockhash']])
|
||||
block_height = block_header['height']
|
||||
if "blockhash" in tx:
|
||||
block_header = self.rpc("getblockheader", [tx["blockhash"]])
|
||||
block_height = block_header["height"]
|
||||
|
||||
rv = {
|
||||
'depth': 0 if 'confirmations' not in tx else tx['confirmations'],
|
||||
'height': block_height}
|
||||
"depth": 0 if "confirmations" not in tx else tx["confirmations"],
|
||||
"height": block_height,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
self._log.debug('getLockTxHeight gettransaction failed: %s, %s', txid.hex(), str(e))
|
||||
self._log.debug(
|
||||
"getLockTxHeight gettransaction failed: %s, %s", txid.hex(), str(e)
|
||||
)
|
||||
return None
|
||||
|
||||
if find_index:
|
||||
tx_obj = self.rpc('decoderawtransaction', [tx['hex']])
|
||||
rv['index'] = find_vout_for_address_from_txobj(tx_obj, dest_address)
|
||||
tx_obj = self.rpc("decoderawtransaction", [tx["hex"]])
|
||||
rv["index"] = find_vout_for_address_from_txobj(tx_obj, dest_address)
|
||||
|
||||
if return_txid:
|
||||
rv['txid'] = txid.hex()
|
||||
rv["txid"] = txid.hex()
|
||||
|
||||
return rv
|
||||
|
||||
def getBlockWithTxns(self, block_hash):
|
||||
# TODO: Bypass decoderawtransaction and getblockheader
|
||||
block = self.rpc('getblock', [block_hash, False])
|
||||
block_header = self.rpc('getblockheader', [block_hash])
|
||||
block = self.rpc("getblock", [block_hash, False])
|
||||
block_header = self.rpc("getblockheader", [block_hash])
|
||||
decoded_block = CBlock()
|
||||
decoded_block = FromHex(decoded_block, block)
|
||||
|
||||
tx_rv = []
|
||||
for tx in decoded_block.vtx:
|
||||
tx_hex = tx.serialize_with_witness().hex()
|
||||
tx_dec = self.rpc('decoderawtransaction', [tx_hex])
|
||||
if 'hex' not in tx_dec:
|
||||
tx_dec['hex'] = tx_hex
|
||||
tx_dec = self.rpc("decoderawtransaction", [tx_hex])
|
||||
if "hex" not in tx_dec:
|
||||
tx_dec["hex"] = tx_hex
|
||||
|
||||
tx_rv.append(tx_dec)
|
||||
|
||||
block_rv = {
|
||||
'hash': block_hash,
|
||||
'previousblockhash': block_header['previousblockhash'],
|
||||
'tx': tx_rv,
|
||||
'confirmations': block_header['confirmations'],
|
||||
'height': block_header['height'],
|
||||
'time': block_header['time'],
|
||||
'version': block_header['version'],
|
||||
'merkleroot': block_header['merkleroot'],
|
||||
"hash": block_hash,
|
||||
"previousblockhash": block_header["previousblockhash"],
|
||||
"tx": tx_rv,
|
||||
"confirmations": block_header["confirmations"],
|
||||
"height": block_header["height"],
|
||||
"time": block_header["time"],
|
||||
"version": block_header["version"],
|
||||
"merkleroot": block_header["merkleroot"],
|
||||
}
|
||||
|
||||
return block_rv
|
||||
|
@ -513,15 +657,30 @@ class NAVInterface(BTCInterface):
|
|||
tx.vout.append(self.txoType()(output_amount, script_pk))
|
||||
return tx.serialize()
|
||||
|
||||
def spendBLockTx(self, chain_b_lock_txid: bytes, address_to: str, kbv: bytes, kbs: bytes, cb_swap_value: int, b_fee: int, restore_height: int, lock_tx_vout=None) -> bytes:
|
||||
self._log.info('spendBLockTx %s:\n', chain_b_lock_txid.hex())
|
||||
wtx = self.rpc('gettransaction', [chain_b_lock_txid.hex(), ])
|
||||
lock_tx = self.loadTx(bytes.fromhex(wtx['hex']))
|
||||
def spendBLockTx(
|
||||
self,
|
||||
chain_b_lock_txid: bytes,
|
||||
address_to: str,
|
||||
kbv: bytes,
|
||||
kbs: bytes,
|
||||
cb_swap_value: int,
|
||||
b_fee: int,
|
||||
restore_height: int,
|
||||
lock_tx_vout=None,
|
||||
) -> bytes:
|
||||
self._log.info("spendBLockTx %s:\n", chain_b_lock_txid.hex())
|
||||
wtx = self.rpc(
|
||||
"gettransaction",
|
||||
[
|
||||
chain_b_lock_txid.hex(),
|
||||
],
|
||||
)
|
||||
lock_tx = self.loadTx(bytes.fromhex(wtx["hex"]))
|
||||
|
||||
Kbs = self.getPubkey(kbs)
|
||||
script_pk = self.getPkDest(Kbs)
|
||||
locked_n = findOutput(lock_tx, script_pk)
|
||||
ensure(locked_n is not None, 'Output not found in tx')
|
||||
ensure(locked_n is not None, "Output not found in tx")
|
||||
pkh_to = self.decodeAddress(address_to)
|
||||
|
||||
tx = CTransaction()
|
||||
|
@ -531,10 +690,16 @@ class NAVInterface(BTCInterface):
|
|||
|
||||
script_sig = self.getInputScriptForPubkeyHash(self.getPubkeyHash(Kbs))
|
||||
|
||||
tx.vin.append(CTxIn(COutPoint(chain_b_lock_txid_int, locked_n),
|
||||
nSequence=0,
|
||||
scriptSig=script_sig))
|
||||
tx.vout.append(self.txoType()(cb_swap_value, self.getScriptForPubkeyHash(pkh_to)))
|
||||
tx.vin.append(
|
||||
CTxIn(
|
||||
COutPoint(chain_b_lock_txid_int, locked_n),
|
||||
nSequence=0,
|
||||
scriptSig=script_sig,
|
||||
)
|
||||
)
|
||||
tx.vout.append(
|
||||
self.txoType()(cb_swap_value, self.getScriptForPubkeyHash(pkh_to))
|
||||
)
|
||||
|
||||
pay_fee = self.getBLockSpendTxFee(tx, b_fee)
|
||||
tx.vout[0].nValue = cb_swap_value - pay_fee
|
||||
|
@ -560,16 +725,20 @@ class NAVInterface(BTCInterface):
|
|||
def findTxnByHash(self, txid_hex: str):
|
||||
# Only works for wallet txns
|
||||
try:
|
||||
rv = self.rpc('gettransaction', [txid_hex])
|
||||
except Exception as ex:
|
||||
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
|
||||
rv = self.rpc("gettransaction", [txid_hex])
|
||||
except Exception as e: # noqa: F841
|
||||
self._log.debug(
|
||||
"findTxnByHash getrawtransaction failed: {}".format(txid_hex)
|
||||
)
|
||||
return None
|
||||
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
|
||||
block_height = self.getBlockHeader(rv['blockhash'])['height']
|
||||
return {'txid': txid_hex, 'amount': 0, 'height': block_height}
|
||||
if "confirmations" in rv and rv["confirmations"] >= self.blocks_confirmed:
|
||||
block_height = self.getBlockHeader(rv["blockhash"])["height"]
|
||||
return {"txid": txid_hex, "amount": 0, "height": block_height}
|
||||
return None
|
||||
|
||||
def createSCLockTx(self, value: int, script: bytearray, vkbv: bytes = None) -> bytes:
|
||||
def createSCLockTx(
|
||||
self, value: int, script: bytearray, vkbv: bytes = None
|
||||
) -> bytes:
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
tx.vout.append(self.txoType()(value, self.getScriptDest(script)))
|
||||
|
@ -580,20 +749,20 @@ class NAVInterface(BTCInterface):
|
|||
feerate_str = self.format_amount(feerate)
|
||||
# TODO: unlock unspents if bid cancelled
|
||||
options = {
|
||||
'lockUnspents': lock_unspents,
|
||||
'feeRate': feerate_str,
|
||||
"lockUnspents": lock_unspents,
|
||||
"feeRate": feerate_str,
|
||||
}
|
||||
rv = self.rpc('fundrawtransaction', [tx_hex, options])
|
||||
rv = self.rpc("fundrawtransaction", [tx_hex, options])
|
||||
|
||||
# Sign transaction then strip witness data to fill scriptsig
|
||||
rv = self.rpc('signrawtransaction', [rv['hex']])
|
||||
rv = self.rpc("signrawtransaction", [rv["hex"]])
|
||||
|
||||
tx_signed = self.loadTx(bytes.fromhex(rv['hex']))
|
||||
tx_signed = self.loadTx(bytes.fromhex(rv["hex"]))
|
||||
if len(tx_signed.vin) != len(tx_signed.wit.vtxinwit):
|
||||
raise ValueError('txn has non segwit input')
|
||||
raise ValueError("txn has non segwit input")
|
||||
for witness_data in tx_signed.wit.vtxinwit:
|
||||
if len(witness_data.scriptWitness.stack) < 2:
|
||||
raise ValueError('txn has non segwit input')
|
||||
raise ValueError("txn has non segwit input")
|
||||
|
||||
return tx_signed.serialize_without_witness()
|
||||
|
||||
|
@ -601,13 +770,23 @@ class NAVInterface(BTCInterface):
|
|||
tx_funded = self.fundTx(tx_bytes.hex(), feerate)
|
||||
return tx_funded
|
||||
|
||||
def createSCLockRefundTx(self, tx_lock_bytes, script_lock, Kal, Kaf, lock1_value, csv_val, tx_fee_rate, vkbv=None):
|
||||
def createSCLockRefundTx(
|
||||
self,
|
||||
tx_lock_bytes,
|
||||
script_lock,
|
||||
Kal,
|
||||
Kaf,
|
||||
lock1_value,
|
||||
csv_val,
|
||||
tx_fee_rate,
|
||||
vkbv=None,
|
||||
):
|
||||
tx_lock = CTransaction()
|
||||
tx_lock = self.loadTx(tx_lock_bytes)
|
||||
|
||||
output_script = self.getScriptDest(script_lock)
|
||||
locked_n = findOutput(tx_lock, output_script)
|
||||
ensure(locked_n is not None, 'Output not found in tx')
|
||||
ensure(locked_n is not None, "Output not found in tx")
|
||||
locked_coin = tx_lock.vout[locked_n].nValue
|
||||
|
||||
tx_lock.rehash()
|
||||
|
@ -616,9 +795,13 @@ class NAVInterface(BTCInterface):
|
|||
refund_script = self.genScriptLockRefundTxScript(Kal, Kaf, csv_val)
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
tx.vin.append(CTxIn(COutPoint(tx_lock_id_int, locked_n),
|
||||
nSequence=lock1_value,
|
||||
scriptSig=self.getScriptScriptSig(script_lock)))
|
||||
tx.vin.append(
|
||||
CTxIn(
|
||||
COutPoint(tx_lock_id_int, locked_n),
|
||||
nSequence=lock1_value,
|
||||
scriptSig=self.getScriptScriptSig(script_lock),
|
||||
)
|
||||
)
|
||||
tx.vout.append(self.txoType()(locked_coin, self.getScriptDest(refund_script)))
|
||||
|
||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
||||
|
@ -628,12 +811,24 @@ class NAVInterface(BTCInterface):
|
|||
tx.vout[0].nValue = locked_coin - pay_fee
|
||||
|
||||
tx.rehash()
|
||||
self._log.info('createSCLockRefundTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.',
|
||||
i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
|
||||
self._log.info(
|
||||
"createSCLockRefundTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
tx_fee_rate,
|
||||
vsize,
|
||||
pay_fee,
|
||||
)
|
||||
|
||||
return tx.serialize(), refund_script, tx.vout[0].nValue
|
||||
|
||||
def createSCLockRefundSpendTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_refund_to, tx_fee_rate, vkbv=None):
|
||||
def createSCLockRefundSpendTx(
|
||||
self,
|
||||
tx_lock_refund_bytes,
|
||||
script_lock_refund,
|
||||
pkh_refund_to,
|
||||
tx_fee_rate,
|
||||
vkbv=None,
|
||||
):
|
||||
# Returns the coinA locked coin to the leader
|
||||
# The follower will sign the multisig path with a signature encumbered by the leader's coinB spend pubkey
|
||||
# If the leader publishes the decrypted signature the leader's coinB spend privatekey will be revealed to the follower
|
||||
|
@ -642,7 +837,7 @@ class NAVInterface(BTCInterface):
|
|||
|
||||
output_script = self.getScriptDest(script_lock_refund)
|
||||
locked_n = findOutput(tx_lock_refund, output_script)
|
||||
ensure(locked_n is not None, 'Output not found in tx')
|
||||
ensure(locked_n is not None, "Output not found in tx")
|
||||
locked_coin = tx_lock_refund.vout[locked_n].nValue
|
||||
|
||||
tx_lock_refund.rehash()
|
||||
|
@ -650,25 +845,46 @@ class NAVInterface(BTCInterface):
|
|||
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
tx.vin.append(CTxIn(COutPoint(tx_lock_refund_hash_int, locked_n),
|
||||
nSequence=0,
|
||||
scriptSig=self.getScriptScriptSig(script_lock_refund)))
|
||||
tx.vin.append(
|
||||
CTxIn(
|
||||
COutPoint(tx_lock_refund_hash_int, locked_n),
|
||||
nSequence=0,
|
||||
scriptSig=self.getScriptScriptSig(script_lock_refund),
|
||||
)
|
||||
)
|
||||
|
||||
tx.vout.append(self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_refund_to)))
|
||||
tx.vout.append(
|
||||
self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_refund_to))
|
||||
)
|
||||
|
||||
dummy_witness_stack = self.getScriptLockRefundSpendTxDummyWitness(script_lock_refund)
|
||||
dummy_witness_stack = self.getScriptLockRefundSpendTxDummyWitness(
|
||||
script_lock_refund
|
||||
)
|
||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||
pay_fee = round(tx_fee_rate * vsize / 1000)
|
||||
tx.vout[0].nValue = locked_coin - pay_fee
|
||||
|
||||
tx.rehash()
|
||||
self._log.info('createSCLockRefundSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.',
|
||||
i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
|
||||
self._log.info(
|
||||
"createSCLockRefundSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
tx_fee_rate,
|
||||
vsize,
|
||||
pay_fee,
|
||||
)
|
||||
|
||||
return tx.serialize()
|
||||
|
||||
def createSCLockRefundSpendToFTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_dest, tx_fee_rate, vkbv=None, kbsf=None):
|
||||
def createSCLockRefundSpendToFTx(
|
||||
self,
|
||||
tx_lock_refund_bytes,
|
||||
script_lock_refund,
|
||||
pkh_dest,
|
||||
tx_fee_rate,
|
||||
vkbv=None,
|
||||
kbsf=None,
|
||||
):
|
||||
# lock refund swipe tx
|
||||
# Sends the coinA locked coin to the follower
|
||||
|
||||
|
@ -676,7 +892,7 @@ class NAVInterface(BTCInterface):
|
|||
|
||||
output_script = self.getScriptDest(script_lock_refund)
|
||||
locked_n = findOutput(tx_lock_refund, output_script)
|
||||
ensure(locked_n is not None, 'Output not found in tx')
|
||||
ensure(locked_n is not None, "Output not found in tx")
|
||||
locked_coin = tx_lock_refund.vout[locked_n].nValue
|
||||
|
||||
A, B, lock2_value, C = extractScriptLockRefundScriptValues(script_lock_refund)
|
||||
|
@ -686,29 +902,44 @@ class NAVInterface(BTCInterface):
|
|||
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
tx.vin.append(CTxIn(COutPoint(tx_lock_refund_hash_int, locked_n),
|
||||
nSequence=lock2_value,
|
||||
scriptSig=self.getScriptScriptSig(script_lock_refund)))
|
||||
tx.vin.append(
|
||||
CTxIn(
|
||||
COutPoint(tx_lock_refund_hash_int, locked_n),
|
||||
nSequence=lock2_value,
|
||||
scriptSig=self.getScriptScriptSig(script_lock_refund),
|
||||
)
|
||||
)
|
||||
|
||||
tx.vout.append(self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_dest)))
|
||||
tx.vout.append(
|
||||
self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_dest))
|
||||
)
|
||||
|
||||
dummy_witness_stack = self.getScriptLockRefundSwipeTxDummyWitness(script_lock_refund)
|
||||
dummy_witness_stack = self.getScriptLockRefundSwipeTxDummyWitness(
|
||||
script_lock_refund
|
||||
)
|
||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||
pay_fee = round(tx_fee_rate * vsize / 1000)
|
||||
tx.vout[0].nValue = locked_coin - pay_fee
|
||||
|
||||
tx.rehash()
|
||||
self._log.info('createSCLockRefundSpendToFTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.',
|
||||
i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
|
||||
self._log.info(
|
||||
"createSCLockRefundSpendToFTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
tx_fee_rate,
|
||||
vsize,
|
||||
pay_fee,
|
||||
)
|
||||
|
||||
return tx.serialize()
|
||||
|
||||
def createSCLockSpendTx(self, tx_lock_bytes, script_lock, pkh_dest, tx_fee_rate, vkbv=None, fee_info={}):
|
||||
def createSCLockSpendTx(
|
||||
self, tx_lock_bytes, script_lock, pkh_dest, tx_fee_rate, vkbv=None, fee_info={}
|
||||
):
|
||||
tx_lock = self.loadTx(tx_lock_bytes)
|
||||
output_script = self.getScriptDest(script_lock)
|
||||
locked_n = findOutput(tx_lock, output_script)
|
||||
ensure(locked_n is not None, 'Output not found in tx')
|
||||
ensure(locked_n is not None, "Output not found in tx")
|
||||
locked_coin = tx_lock.vout[locked_n].nValue
|
||||
|
||||
tx_lock.rehash()
|
||||
|
@ -716,10 +947,16 @@ class NAVInterface(BTCInterface):
|
|||
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
tx.vin.append(CTxIn(COutPoint(tx_lock_id_int, locked_n),
|
||||
scriptSig=self.getScriptScriptSig(script_lock)))
|
||||
tx.vin.append(
|
||||
CTxIn(
|
||||
COutPoint(tx_lock_id_int, locked_n),
|
||||
scriptSig=self.getScriptScriptSig(script_lock),
|
||||
)
|
||||
)
|
||||
|
||||
tx.vout.append(self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_dest)))
|
||||
tx.vout.append(
|
||||
self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_dest))
|
||||
)
|
||||
|
||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||
|
@ -727,13 +964,18 @@ class NAVInterface(BTCInterface):
|
|||
pay_fee = round(tx_fee_rate * vsize / 1000)
|
||||
tx.vout[0].nValue = locked_coin - pay_fee
|
||||
|
||||
fee_info['fee_paid'] = pay_fee
|
||||
fee_info['rate_used'] = tx_fee_rate
|
||||
fee_info['witness_bytes'] = witness_bytes
|
||||
fee_info['vsize'] = vsize
|
||||
fee_info["fee_paid"] = pay_fee
|
||||
fee_info["rate_used"] = tx_fee_rate
|
||||
fee_info["witness_bytes"] = witness_bytes
|
||||
fee_info["vsize"] = vsize
|
||||
|
||||
tx.rehash()
|
||||
self._log.info('createSCLockSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.',
|
||||
i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
|
||||
self._log.info(
|
||||
"createSCLockSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
tx_fee_rate,
|
||||
vsize,
|
||||
pay_fee,
|
||||
)
|
||||
|
||||
return tx.serialize()
|
||||
|
|
|
@ -14,26 +14,38 @@ class NMCInterface(BTCInterface):
|
|||
def coin_type():
|
||||
return Coins.NMC
|
||||
|
||||
def getLockTxHeight(self, txid, dest_address, bid_amount, rescan_from, find_index: bool = False, vout: int = -1):
|
||||
self._log.debug('[rm] scantxoutset start') # scantxoutset is slow
|
||||
ro = self.rpc('scantxoutset', ['start', ['addr({})'.format(dest_address)]]) # TODO: Use combo(address) where possible
|
||||
self._log.debug('[rm] scantxoutset end')
|
||||
def getLockTxHeight(
|
||||
self,
|
||||
txid,
|
||||
dest_address,
|
||||
bid_amount,
|
||||
rescan_from,
|
||||
find_index: bool = False,
|
||||
vout: int = -1,
|
||||
):
|
||||
self._log.debug("[rm] scantxoutset start") # scantxoutset is slow
|
||||
ro = self.rpc(
|
||||
"scantxoutset", ["start", ["addr({})".format(dest_address)]]
|
||||
) # TODO: Use combo(address) where possible
|
||||
self._log.debug("[rm] scantxoutset end")
|
||||
return_txid = True if txid is None else False
|
||||
for o in ro['unspents']:
|
||||
if txid and o['txid'] != txid.hex():
|
||||
for o in ro["unspents"]:
|
||||
if txid and o["txid"] != txid.hex():
|
||||
continue
|
||||
# Verify amount
|
||||
if self.make_int(o['amount']) != int(bid_amount):
|
||||
self._log.warning('Found output to lock tx address of incorrect value: %s, %s', str(o['amount']), o['txid'])
|
||||
if self.make_int(o["amount"]) != int(bid_amount):
|
||||
self._log.warning(
|
||||
"Found output to lock tx address of incorrect value: %s, %s",
|
||||
str(o["amount"]),
|
||||
o["txid"],
|
||||
)
|
||||
continue
|
||||
|
||||
rv = {
|
||||
'depth': 0,
|
||||
'height': o['height']}
|
||||
if o['height'] > 0:
|
||||
rv['depth'] = ro['height'] - o['height']
|
||||
rv = {"depth": 0, "height": o["height"]}
|
||||
if o["height"] > 0:
|
||||
rv["depth"] = ro["height"] - o["height"]
|
||||
if find_index:
|
||||
rv['index'] = o['vout']
|
||||
rv["index"] = o["vout"]
|
||||
if return_txid:
|
||||
rv['txid'] = o['txid']
|
||||
rv["txid"] = o["txid"]
|
||||
return rv
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -6,8 +6,7 @@
|
|||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from .btc import BTCInterface
|
||||
from basicswap.contrib.test_framework.messages import (
|
||||
CTxOut)
|
||||
from basicswap.contrib.test_framework.messages import CTxOut
|
||||
|
||||
|
||||
class PassthroughBTCInterface(BTCInterface):
|
||||
|
@ -15,5 +14,5 @@ class PassthroughBTCInterface(BTCInterface):
|
|||
super().__init__(coin_settings, network)
|
||||
self.txoType = CTxOut
|
||||
self._network = network
|
||||
self.blocks_confirmed = coin_settings['blocks_confirmed']
|
||||
self.setConfTarget(coin_settings['conf_target'])
|
||||
self.blocks_confirmed = coin_settings["blocks_confirmed"]
|
||||
self.setConfTarget(coin_settings["conf_target"])
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022 tecnovert
|
||||
# Copyright (c) 2024 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -11,11 +12,7 @@ from .btc import BTCInterface
|
|||
from basicswap.rpc import make_rpc_func
|
||||
from basicswap.chainparams import Coins
|
||||
from basicswap.util.address import decodeAddress
|
||||
from .contrib.pivx_test_framework.messages import (
|
||||
CBlock,
|
||||
ToHex,
|
||||
FromHex,
|
||||
CTransaction)
|
||||
from .contrib.pivx_test_framework.messages import CBlock, ToHex, FromHex, CTransaction
|
||||
from basicswap.contrib.test_framework.script import (
|
||||
CScript,
|
||||
OP_DUP,
|
||||
|
@ -33,65 +30,79 @@ class PIVXInterface(BTCInterface):
|
|||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(PIVXInterface, self).__init__(coin_settings, network, swap_client)
|
||||
# No multiwallet support
|
||||
self.rpc_wallet = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host)
|
||||
self.rpc_wallet = make_rpc_func(
|
||||
self._rpcport, self._rpcauth, host=self._rpc_host
|
||||
)
|
||||
|
||||
def checkWallets(self) -> int:
|
||||
return 1
|
||||
|
||||
def signTxWithWallet(self, tx):
|
||||
rv = self.rpc('signrawtransaction', [tx.hex()])
|
||||
return bytes.fromhex(rv['hex'])
|
||||
rv = self.rpc("signrawtransaction", [tx.hex()])
|
||||
return bytes.fromhex(rv["hex"])
|
||||
|
||||
def createRawFundedTransaction(self, addr_to: str, amount: int, sub_fee: bool = False, lock_unspents: bool = True) -> str:
|
||||
txn = self.rpc('createrawtransaction', [[], {addr_to: self.format_amount(amount)}])
|
||||
def createRawFundedTransaction(
|
||||
self,
|
||||
addr_to: str,
|
||||
amount: int,
|
||||
sub_fee: bool = False,
|
||||
lock_unspents: bool = True,
|
||||
) -> str:
|
||||
txn = self.rpc(
|
||||
"createrawtransaction", [[], {addr_to: self.format_amount(amount)}]
|
||||
)
|
||||
fee_rate, fee_src = self.get_fee_rate(self._conf_target)
|
||||
self._log.debug(f'Fee rate: {fee_rate}, source: {fee_src}, block target: {self._conf_target}')
|
||||
self._log.debug(
|
||||
f"Fee rate: {fee_rate}, source: {fee_src}, block target: {self._conf_target}"
|
||||
)
|
||||
options = {
|
||||
'lockUnspents': lock_unspents,
|
||||
'feeRate': fee_rate,
|
||||
"lockUnspents": lock_unspents,
|
||||
"feeRate": fee_rate,
|
||||
}
|
||||
if sub_fee:
|
||||
options['subtractFeeFromOutputs'] = [0,]
|
||||
return self.rpc('fundrawtransaction', [txn, options])['hex']
|
||||
options["subtractFeeFromOutputs"] = [
|
||||
0,
|
||||
]
|
||||
return self.rpc("fundrawtransaction", [txn, options])["hex"]
|
||||
|
||||
def createRawSignedTransaction(self, addr_to, amount) -> str:
|
||||
txn_funded = self.createRawFundedTransaction(addr_to, amount)
|
||||
return self.rpc('signrawtransaction', [txn_funded])['hex']
|
||||
return self.rpc("signrawtransaction", [txn_funded])["hex"]
|
||||
|
||||
def decodeAddress(self, address):
|
||||
return decodeAddress(address)[1:]
|
||||
|
||||
def getBlockWithTxns(self, block_hash):
|
||||
# TODO: Bypass decoderawtransaction and getblockheader
|
||||
block = self.rpc('getblock', [block_hash, False])
|
||||
block_header = self.rpc('getblockheader', [block_hash])
|
||||
block = self.rpc("getblock", [block_hash, False])
|
||||
block_header = self.rpc("getblockheader", [block_hash])
|
||||
decoded_block = CBlock()
|
||||
decoded_block = FromHex(decoded_block, block)
|
||||
|
||||
tx_rv = []
|
||||
for tx in decoded_block.vtx:
|
||||
tx_dec = self.rpc('decoderawtransaction', [ToHex(tx)])
|
||||
tx_dec = self.rpc("decoderawtransaction", [ToHex(tx)])
|
||||
tx_rv.append(tx_dec)
|
||||
|
||||
block_rv = {
|
||||
'hash': block_hash,
|
||||
'previousblockhash': block_header['previousblockhash'],
|
||||
'tx': tx_rv,
|
||||
'confirmations': block_header['confirmations'],
|
||||
'height': block_header['height'],
|
||||
'time': block_header['time'],
|
||||
'version': block_header['version'],
|
||||
'merkleroot': block_header['merkleroot'],
|
||||
"hash": block_hash,
|
||||
"previousblockhash": block_header["previousblockhash"],
|
||||
"tx": tx_rv,
|
||||
"confirmations": block_header["confirmations"],
|
||||
"height": block_header["height"],
|
||||
"time": block_header["time"],
|
||||
"version": block_header["version"],
|
||||
"merkleroot": block_header["merkleroot"],
|
||||
}
|
||||
|
||||
return block_rv
|
||||
|
||||
def withdrawCoin(self, value, addr_to, subfee):
|
||||
params = [addr_to, value, '', '', subfee]
|
||||
return self.rpc('sendtoaddress', params)
|
||||
params = [addr_to, value, "", "", subfee]
|
||||
return self.rpc("sendtoaddress", params)
|
||||
|
||||
def getSpendableBalance(self) -> int:
|
||||
return self.make_int(self.rpc('getwalletinfo')['balance'])
|
||||
return self.make_int(self.rpc("getwalletinfo")["balance"])
|
||||
|
||||
def loadTx(self, tx_bytes):
|
||||
# Load tx from bytes to internal representation
|
||||
|
@ -107,22 +118,35 @@ class PIVXInterface(BTCInterface):
|
|||
add_bytes = 107
|
||||
size = len(tx.serialize_with_witness()) + add_bytes
|
||||
pay_fee = round(fee_rate * size / 1000)
|
||||
self._log.info(f'BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
|
||||
self._log.info(
|
||||
f"BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}."
|
||||
)
|
||||
return pay_fee
|
||||
|
||||
def signTxWithKey(self, tx: bytes, key: bytes) -> bytes:
|
||||
key_wif = self.encodeKey(key)
|
||||
rv = self.rpc('signrawtransaction', [tx.hex(), [], [key_wif, ]])
|
||||
return bytes.fromhex(rv['hex'])
|
||||
rv = self.rpc(
|
||||
"signrawtransaction",
|
||||
[
|
||||
tx.hex(),
|
||||
[],
|
||||
[
|
||||
key_wif,
|
||||
],
|
||||
],
|
||||
)
|
||||
return bytes.fromhex(rv["hex"])
|
||||
|
||||
def findTxnByHash(self, txid_hex: str):
|
||||
# Only works for wallet txns
|
||||
try:
|
||||
rv = self.rpc('gettransaction', [txid_hex])
|
||||
except Exception as ex:
|
||||
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
|
||||
rv = self.rpc("gettransaction", [txid_hex])
|
||||
except Exception as e: # noqa: F841
|
||||
self._log.debug(
|
||||
"findTxnByHash getrawtransaction failed: {}".format(txid_hex)
|
||||
)
|
||||
return None
|
||||
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
|
||||
block_height = self.getBlockHeader(rv['blockhash'])['height']
|
||||
return {'txid': txid_hex, 'amount': 0, 'height': block_height}
|
||||
if "confirmations" in rv and rv["confirmations"] >= self.blocks_confirmed:
|
||||
block_height = self.getBlockHeader(rv["blockhash"])["height"]
|
||||
return {"txid": txid_hex, "amount": 0, "height": block_height}
|
||||
return None
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020-2024 tecnovert
|
||||
# Copyright (c) 2024 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -26,16 +27,9 @@ from coincurve.dleag import (
|
|||
from basicswap.interface.base import (
|
||||
Curves,
|
||||
)
|
||||
from basicswap.util import (
|
||||
i2b, b2i, b2h,
|
||||
dumpj,
|
||||
ensure,
|
||||
TemporaryError)
|
||||
from basicswap.util.network import (
|
||||
is_private_ip_address)
|
||||
from basicswap.rpc_xmr import (
|
||||
make_xmr_rpc_func,
|
||||
make_xmr_rpc2_func)
|
||||
from basicswap.util import i2b, b2i, b2h, dumpj, ensure, TemporaryError
|
||||
from basicswap.util.network import is_private_ip_address
|
||||
from basicswap.rpc_xmr import make_xmr_rpc_func, make_xmr_rpc2_func
|
||||
from basicswap.chainparams import XMR_COIN, Coins
|
||||
from basicswap.interface.base import CoinInterface
|
||||
|
||||
|
@ -75,7 +69,7 @@ class XMRInterface(CoinInterface):
|
|||
|
||||
@staticmethod
|
||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||
raise ValueError('Not possible')
|
||||
raise ValueError("Not possible")
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_b_lock_spend_tx_vsize() -> int:
|
||||
|
@ -84,62 +78,93 @@ class XMRInterface(CoinInterface):
|
|||
|
||||
def is_transient_error(self, ex) -> bool:
|
||||
str_error: str = str(ex).lower()
|
||||
if 'failed to get output distribution' in str_error:
|
||||
if "failed to get output distribution" in str_error:
|
||||
return True
|
||||
return super().is_transient_error(ex)
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super().__init__(network)
|
||||
|
||||
self._addr_prefix = self.chainparams_network()['address_prefix']
|
||||
self._addr_prefix = self.chainparams_network()["address_prefix"]
|
||||
|
||||
self.blocks_confirmed = coin_settings['blocks_confirmed']
|
||||
self._restore_height = coin_settings.get('restore_height', 0)
|
||||
self.setFeePriority(coin_settings.get('fee_priority', 0))
|
||||
self.blocks_confirmed = coin_settings["blocks_confirmed"]
|
||||
self._restore_height = coin_settings.get("restore_height", 0)
|
||||
self.setFeePriority(coin_settings.get("fee_priority", 0))
|
||||
self._sc = swap_client
|
||||
self._log = self._sc.log if self._sc and self._sc.log else logging
|
||||
self._wallet_password = None
|
||||
self._have_checked_seed = False
|
||||
|
||||
daemon_login = None
|
||||
if coin_settings.get('rpcuser', '') != '':
|
||||
daemon_login = (coin_settings.get('rpcuser', ''), coin_settings.get('rpcpassword', ''))
|
||||
if coin_settings.get("rpcuser", "") != "":
|
||||
daemon_login = (
|
||||
coin_settings.get("rpcuser", ""),
|
||||
coin_settings.get("rpcpassword", ""),
|
||||
)
|
||||
|
||||
rpchost = coin_settings.get('rpchost', '127.0.0.1')
|
||||
rpchost = coin_settings.get("rpchost", "127.0.0.1")
|
||||
proxy_host = None
|
||||
proxy_port = None
|
||||
# Connect to the daemon over a proxy if not running locally
|
||||
if swap_client:
|
||||
chain_client_settings = swap_client.getChainClientSettings(self.coin_type())
|
||||
manage_daemon: bool = chain_client_settings['manage_daemon']
|
||||
manage_daemon: bool = chain_client_settings["manage_daemon"]
|
||||
if swap_client.use_tor_proxy:
|
||||
if manage_daemon is False:
|
||||
log_str: str = ''
|
||||
have_cc_tor_opt = 'use_tor' in chain_client_settings
|
||||
if have_cc_tor_opt and chain_client_settings['use_tor'] is False:
|
||||
log_str = ' bypassing proxy (use_tor false for XMR)'
|
||||
log_str: str = ""
|
||||
have_cc_tor_opt = "use_tor" in chain_client_settings
|
||||
if have_cc_tor_opt and chain_client_settings["use_tor"] is False:
|
||||
log_str = " bypassing proxy (use_tor false for XMR)"
|
||||
elif have_cc_tor_opt is False and is_private_ip_address(rpchost):
|
||||
log_str = ' bypassing proxy (private ip address)'
|
||||
log_str = " bypassing proxy (private ip address)"
|
||||
else:
|
||||
proxy_host = swap_client.tor_proxy_host
|
||||
proxy_port = swap_client.tor_proxy_port
|
||||
log_str = f' through proxy at {proxy_host}'
|
||||
self._log.info(f'Connecting to remote {self.coin_name()} daemon at {rpchost}{log_str}.')
|
||||
log_str = f" through proxy at {proxy_host}"
|
||||
self._log.info(
|
||||
f"Connecting to remote {self.coin_name()} daemon at {rpchost}{log_str}."
|
||||
)
|
||||
else:
|
||||
self._log.info(f'Not connecting to local {self.coin_name()} daemon through proxy.')
|
||||
self._log.info(
|
||||
f"Not connecting to local {self.coin_name()} daemon through proxy."
|
||||
)
|
||||
elif manage_daemon is False:
|
||||
self._log.info(f'Connecting to remote {self.coin_name()} daemon at {rpchost}.')
|
||||
self._log.info(
|
||||
f"Connecting to remote {self.coin_name()} daemon at {rpchost}."
|
||||
)
|
||||
|
||||
self._rpctimeout = coin_settings.get('rpctimeout', 60)
|
||||
self._walletrpctimeout = coin_settings.get('walletrpctimeout', 120)
|
||||
self._walletrpctimeoutlong = coin_settings.get('walletrpctimeoutlong', 600)
|
||||
self._rpctimeout = coin_settings.get("rpctimeout", 60)
|
||||
self._walletrpctimeout = coin_settings.get("walletrpctimeout", 120)
|
||||
self._walletrpctimeoutlong = coin_settings.get("walletrpctimeoutlong", 600)
|
||||
|
||||
self.rpc = make_xmr_rpc_func(coin_settings['rpcport'], daemon_login, host=rpchost, proxy_host=proxy_host, proxy_port=proxy_port, default_timeout=self._rpctimeout, tag='Node(j) ')
|
||||
self.rpc2 = make_xmr_rpc2_func(coin_settings['rpcport'], daemon_login, host=rpchost, proxy_host=proxy_host, proxy_port=proxy_port, default_timeout=self._rpctimeout, tag='Node ') # non-json endpoint
|
||||
self.rpc_wallet = make_xmr_rpc_func(coin_settings['walletrpcport'], coin_settings['walletrpcauth'], host=coin_settings.get('walletrpchost', '127.0.0.1'), default_timeout=self._walletrpctimeout, tag='Wallet ')
|
||||
self.rpc = make_xmr_rpc_func(
|
||||
coin_settings["rpcport"],
|
||||
daemon_login,
|
||||
host=rpchost,
|
||||
proxy_host=proxy_host,
|
||||
proxy_port=proxy_port,
|
||||
default_timeout=self._rpctimeout,
|
||||
tag="Node(j) ",
|
||||
)
|
||||
self.rpc2 = make_xmr_rpc2_func(
|
||||
coin_settings["rpcport"],
|
||||
daemon_login,
|
||||
host=rpchost,
|
||||
proxy_host=proxy_host,
|
||||
proxy_port=proxy_port,
|
||||
default_timeout=self._rpctimeout,
|
||||
tag="Node ",
|
||||
) # non-json endpoint
|
||||
self.rpc_wallet = make_xmr_rpc_func(
|
||||
coin_settings["walletrpcport"],
|
||||
coin_settings["walletrpcauth"],
|
||||
host=coin_settings.get("walletrpchost", "127.0.0.1"),
|
||||
default_timeout=self._walletrpctimeout,
|
||||
tag="Wallet ",
|
||||
)
|
||||
|
||||
def setFeePriority(self, new_priority):
|
||||
ensure(new_priority >= 0 and new_priority < 4, 'Invalid fee_priority value')
|
||||
ensure(new_priority >= 0 and new_priority < 4, "Invalid fee_priority value")
|
||||
self._fee_priority = new_priority
|
||||
|
||||
def setWalletFilename(self, wallet_filename):
|
||||
|
@ -147,29 +172,31 @@ class XMRInterface(CoinInterface):
|
|||
|
||||
def createWallet(self, params):
|
||||
if self._wallet_password is not None:
|
||||
params['password'] = self._wallet_password
|
||||
rv = self.rpc_wallet('generate_from_keys', params)
|
||||
self._log.info('generate_from_keys %s', dumpj(rv))
|
||||
params["password"] = self._wallet_password
|
||||
rv = self.rpc_wallet("generate_from_keys", params)
|
||||
self._log.info("generate_from_keys %s", dumpj(rv))
|
||||
|
||||
def openWallet(self, filename):
|
||||
params = {'filename': filename}
|
||||
params = {"filename": filename}
|
||||
if self._wallet_password is not None:
|
||||
params['password'] = self._wallet_password
|
||||
params["password"] = self._wallet_password
|
||||
|
||||
try:
|
||||
# Can't reopen the same wallet in windows, !is_keys_file_locked()
|
||||
self.rpc_wallet('close_wallet')
|
||||
self.rpc_wallet("close_wallet")
|
||||
except Exception:
|
||||
pass
|
||||
self.rpc_wallet('open_wallet', params)
|
||||
self.rpc_wallet("open_wallet", params)
|
||||
|
||||
def initialiseWallet(self, key_view: bytes, key_spend: bytes, restore_height=None) -> None:
|
||||
def initialiseWallet(
|
||||
self, key_view: bytes, key_spend: bytes, restore_height=None
|
||||
) -> None:
|
||||
with self._mx_wallet:
|
||||
try:
|
||||
self.openWallet(self._wallet_filename)
|
||||
# TODO: Check address
|
||||
return # Wallet exists
|
||||
except Exception as e:
|
||||
except Exception as e: # noqa: F841
|
||||
pass
|
||||
|
||||
Kbv = self.getPubkey(key_view)
|
||||
|
@ -177,11 +204,11 @@ class XMRInterface(CoinInterface):
|
|||
address_b58 = xmr_util.encode_address(Kbv, Kbs, self._addr_prefix)
|
||||
|
||||
params = {
|
||||
'filename': self._wallet_filename,
|
||||
'address': address_b58,
|
||||
'viewkey': b2h(key_view[::-1]),
|
||||
'spendkey': b2h(key_spend[::-1]),
|
||||
'restore_height': self._restore_height,
|
||||
"filename": self._wallet_filename,
|
||||
"address": address_b58,
|
||||
"viewkey": b2h(key_view[::-1]),
|
||||
"spendkey": b2h(key_spend[::-1]),
|
||||
"restore_height": self._restore_height,
|
||||
}
|
||||
self.createWallet(params)
|
||||
self.openWallet(self._wallet_filename)
|
||||
|
@ -191,84 +218,95 @@ class XMRInterface(CoinInterface):
|
|||
self.openWallet(self._wallet_filename)
|
||||
|
||||
def testDaemonRPC(self, with_wallet=True) -> None:
|
||||
self.rpc_wallet('get_languages')
|
||||
self.rpc_wallet("get_languages")
|
||||
|
||||
def getDaemonVersion(self):
|
||||
return self.rpc_wallet('get_version')['version']
|
||||
return self.rpc_wallet("get_version")["version"]
|
||||
|
||||
def getBlockchainInfo(self):
|
||||
get_height = self.rpc2('get_height', timeout=self._rpctimeout)
|
||||
get_height = self.rpc2("get_height", timeout=self._rpctimeout)
|
||||
rv = {
|
||||
'blocks': get_height['height'],
|
||||
'verificationprogress': 0.0,
|
||||
"blocks": get_height["height"],
|
||||
"verificationprogress": 0.0,
|
||||
}
|
||||
|
||||
try:
|
||||
# get_block_count.block_count is how many blocks are in the longest chain known to the node.
|
||||
# get_block_count returns "Internal error" if bootstrap-daemon is active
|
||||
if get_height['untrusted'] is True:
|
||||
rv['bootstrapping'] = True
|
||||
get_info = self.rpc2('get_info', timeout=self._rpctimeout)
|
||||
if 'height_without_bootstrap' in get_info:
|
||||
rv['blocks'] = get_info['height_without_bootstrap']
|
||||
if get_height["untrusted"] is True:
|
||||
rv["bootstrapping"] = True
|
||||
get_info = self.rpc2("get_info", timeout=self._rpctimeout)
|
||||
if "height_without_bootstrap" in get_info:
|
||||
rv["blocks"] = get_info["height_without_bootstrap"]
|
||||
|
||||
rv['known_block_count'] = get_info['height']
|
||||
if rv['known_block_count'] > rv['blocks']:
|
||||
rv['verificationprogress'] = rv['blocks'] / rv['known_block_count']
|
||||
rv["known_block_count"] = get_info["height"]
|
||||
if rv["known_block_count"] > rv["blocks"]:
|
||||
rv["verificationprogress"] = rv["blocks"] / rv["known_block_count"]
|
||||
else:
|
||||
rv['known_block_count'] = self.rpc('get_block_count', timeout=self._rpctimeout)['count']
|
||||
rv['verificationprogress'] = rv['blocks'] / rv['known_block_count']
|
||||
rv["known_block_count"] = self.rpc(
|
||||
"get_block_count", timeout=self._rpctimeout
|
||||
)["count"]
|
||||
rv["verificationprogress"] = rv["blocks"] / rv["known_block_count"]
|
||||
except Exception as e:
|
||||
self._log.warning(f'{self.ticker_str()} get_block_count failed with: {e}')
|
||||
rv['verificationprogress'] = 0.0
|
||||
self._log.warning(f"{self.ticker_str()} get_block_count failed with: {e}")
|
||||
rv["verificationprogress"] = 0.0
|
||||
|
||||
return rv
|
||||
|
||||
def getChainHeight(self):
|
||||
return self.rpc2('get_height', timeout=self._rpctimeout)['height']
|
||||
return self.rpc2("get_height", timeout=self._rpctimeout)["height"]
|
||||
|
||||
def getWalletInfo(self):
|
||||
with self._mx_wallet:
|
||||
try:
|
||||
self.openWallet(self._wallet_filename)
|
||||
except Exception as e:
|
||||
if 'Failed to open wallet' in str(e):
|
||||
rv = {'encrypted': True, 'locked': True, 'balance': 0, 'unconfirmed_balance': 0}
|
||||
if "Failed to open wallet" in str(e):
|
||||
rv = {
|
||||
"encrypted": True,
|
||||
"locked": True,
|
||||
"balance": 0,
|
||||
"unconfirmed_balance": 0,
|
||||
}
|
||||
return rv
|
||||
raise e
|
||||
|
||||
rv = {}
|
||||
self.rpc_wallet('refresh')
|
||||
balance_info = self.rpc_wallet('get_balance')
|
||||
self.rpc_wallet("refresh")
|
||||
balance_info = self.rpc_wallet("get_balance")
|
||||
|
||||
rv['balance'] = self.format_amount(balance_info['unlocked_balance'])
|
||||
rv['unconfirmed_balance'] = self.format_amount(balance_info['balance'] - balance_info['unlocked_balance'])
|
||||
rv['encrypted'] = False if self._wallet_password is None else True
|
||||
rv['locked'] = False
|
||||
rv["balance"] = self.format_amount(balance_info["unlocked_balance"])
|
||||
rv["unconfirmed_balance"] = self.format_amount(
|
||||
balance_info["balance"] - balance_info["unlocked_balance"]
|
||||
)
|
||||
rv["encrypted"] = False if self._wallet_password is None else True
|
||||
rv["locked"] = False
|
||||
return rv
|
||||
|
||||
def getMainWalletAddress(self) -> str:
|
||||
with self._mx_wallet:
|
||||
self.openWallet(self._wallet_filename)
|
||||
return self.rpc_wallet('get_address')['address']
|
||||
return self.rpc_wallet("get_address")["address"]
|
||||
|
||||
def getNewAddress(self, placeholder) -> str:
|
||||
with self._mx_wallet:
|
||||
self.openWallet(self._wallet_filename)
|
||||
new_address = self.rpc_wallet('create_address', {'account_index': 0})['address']
|
||||
self.rpc_wallet('store')
|
||||
new_address = self.rpc_wallet("create_address", {"account_index": 0})[
|
||||
"address"
|
||||
]
|
||||
self.rpc_wallet("store")
|
||||
return new_address
|
||||
|
||||
def get_fee_rate(self, conf_target: int = 2):
|
||||
# fees - array of unsigned int; Represents the base fees at different priorities [slow, normal, fast, fastest].
|
||||
fee_est = self.rpc('get_fee_estimate')
|
||||
fee_est = self.rpc("get_fee_estimate")
|
||||
if conf_target <= 1:
|
||||
conf_target = 1 # normal
|
||||
else:
|
||||
conf_target = 0 # slow
|
||||
fee_per_k_bytes = fee_est['fees'][conf_target] * 1000
|
||||
fee_per_k_bytes = fee_est["fees"][conf_target] * 1000
|
||||
|
||||
return float(self.format_amount(fee_per_k_bytes)), 'get_fee_estimate'
|
||||
return float(self.format_amount(fee_per_k_bytes)), "get_fee_estimate"
|
||||
|
||||
def getNewSecretKey(self) -> bytes:
|
||||
# Note: Returned bytes are in big endian order
|
||||
|
@ -299,7 +337,7 @@ class XMRInterface(CoinInterface):
|
|||
|
||||
def verifyKey(self, k: int) -> bool:
|
||||
i = b2i(k)
|
||||
return (i < edf.l and i > 8)
|
||||
return i < edf.l and i > 8
|
||||
|
||||
def verifyPubkey(self, pubkey_bytes):
|
||||
# Calls ed25519_decode_check_point() in secp256k1
|
||||
|
@ -325,45 +363,59 @@ class XMRInterface(CoinInterface):
|
|||
def encodeSharedAddress(self, Kbv: bytes, Kbs: bytes) -> str:
|
||||
return xmr_util.encode_address(Kbv, Kbs, self._addr_prefix)
|
||||
|
||||
def publishBLockTx(self, kbv: bytes, Kbs: bytes, output_amount: int, feerate: int, unlock_time: int = 0) -> bytes:
|
||||
def publishBLockTx(
|
||||
self,
|
||||
kbv: bytes,
|
||||
Kbs: bytes,
|
||||
output_amount: int,
|
||||
feerate: int,
|
||||
unlock_time: int = 0,
|
||||
) -> bytes:
|
||||
with self._mx_wallet:
|
||||
self.openWallet(self._wallet_filename)
|
||||
self.rpc_wallet('refresh')
|
||||
self.rpc_wallet("refresh")
|
||||
|
||||
Kbv = self.getPubkey(kbv)
|
||||
shared_addr = xmr_util.encode_address(Kbv, Kbs, self._addr_prefix)
|
||||
|
||||
params = {'destinations': [{'amount': output_amount, 'address': shared_addr}], 'unlock_time': unlock_time}
|
||||
params = {
|
||||
"destinations": [{"amount": output_amount, "address": shared_addr}],
|
||||
"unlock_time": unlock_time,
|
||||
}
|
||||
if self._fee_priority > 0:
|
||||
params['priority'] = self._fee_priority
|
||||
rv = self.rpc_wallet('transfer', params)
|
||||
self._log.info('publishBLockTx %s to address_b58 %s', rv['tx_hash'], shared_addr)
|
||||
tx_hash = bytes.fromhex(rv['tx_hash'])
|
||||
params["priority"] = self._fee_priority
|
||||
rv = self.rpc_wallet("transfer", params)
|
||||
self._log.info(
|
||||
"publishBLockTx %s to address_b58 %s", rv["tx_hash"], shared_addr
|
||||
)
|
||||
tx_hash = bytes.fromhex(rv["tx_hash"])
|
||||
|
||||
return tx_hash
|
||||
|
||||
def findTxB(self, kbv, Kbs, cb_swap_value, cb_block_confirmed, restore_height, bid_sender):
|
||||
def findTxB(
|
||||
self, kbv, Kbs, cb_swap_value, cb_block_confirmed, restore_height, bid_sender
|
||||
):
|
||||
with self._mx_wallet:
|
||||
Kbv = self.getPubkey(kbv)
|
||||
address_b58 = xmr_util.encode_address(Kbv, Kbs, self._addr_prefix)
|
||||
|
||||
kbv_le = kbv[::-1]
|
||||
params = {
|
||||
'restore_height': restore_height,
|
||||
'filename': address_b58,
|
||||
'address': address_b58,
|
||||
'viewkey': b2h(kbv_le),
|
||||
"restore_height": restore_height,
|
||||
"filename": address_b58,
|
||||
"address": address_b58,
|
||||
"viewkey": b2h(kbv_le),
|
||||
}
|
||||
|
||||
try:
|
||||
self.openWallet(address_b58)
|
||||
except Exception as e:
|
||||
except Exception as e: # noqa: F841
|
||||
self.createWallet(params)
|
||||
self.openWallet(address_b58)
|
||||
|
||||
self.rpc_wallet('refresh', timeout=self._walletrpctimeoutlong)
|
||||
self.rpc_wallet("refresh", timeout=self._walletrpctimeoutlong)
|
||||
|
||||
'''
|
||||
"""
|
||||
# Debug
|
||||
try:
|
||||
current_height = self.rpc_wallet('get_height')['height']
|
||||
|
@ -372,137 +424,222 @@ class XMRInterface(CoinInterface):
|
|||
self._log.info('rpc failed %s', str(e))
|
||||
current_height = None # If the transfer is available it will be deep enough
|
||||
# and (current_height is None or current_height - transfer['block_height'] > cb_block_confirmed):
|
||||
'''
|
||||
params = {'transfer_type': 'available'}
|
||||
transfers = self.rpc_wallet('incoming_transfers', params)
|
||||
"""
|
||||
params = {"transfer_type": "available"}
|
||||
transfers = self.rpc_wallet("incoming_transfers", params)
|
||||
rv = None
|
||||
if 'transfers' in transfers:
|
||||
for transfer in transfers['transfers']:
|
||||
if "transfers" in transfers:
|
||||
for transfer in transfers["transfers"]:
|
||||
# unlocked <- wallet->is_transfer_unlocked() checks unlock_time and CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE
|
||||
if not transfer['unlocked']:
|
||||
full_tx = self.rpc_wallet('get_transfer_by_txid', {'txid': transfer['tx_hash']})
|
||||
unlock_time = full_tx['transfer']['unlock_time']
|
||||
if not transfer["unlocked"]:
|
||||
full_tx = self.rpc_wallet(
|
||||
"get_transfer_by_txid", {"txid": transfer["tx_hash"]}
|
||||
)
|
||||
unlock_time = full_tx["transfer"]["unlock_time"]
|
||||
if unlock_time != 0:
|
||||
self._log.warning('Coin b lock txn is locked: {}, unlock_time {}'.format(transfer['tx_hash'], unlock_time))
|
||||
self._log.warning(
|
||||
"Coin b lock txn is locked: {}, unlock_time {}".format(
|
||||
transfer["tx_hash"], unlock_time
|
||||
)
|
||||
)
|
||||
rv = -1
|
||||
continue
|
||||
if transfer['amount'] == cb_swap_value:
|
||||
return {'txid': transfer['tx_hash'], 'amount': transfer['amount'], 'height': 0 if 'block_height' not in transfer else transfer['block_height']}
|
||||
if transfer["amount"] == cb_swap_value:
|
||||
return {
|
||||
"txid": transfer["tx_hash"],
|
||||
"amount": transfer["amount"],
|
||||
"height": (
|
||||
0
|
||||
if "block_height" not in transfer
|
||||
else transfer["block_height"]
|
||||
),
|
||||
}
|
||||
else:
|
||||
self._log.warning('Incorrect amount detected for coin b lock txn: {}'.format(transfer['tx_hash']))
|
||||
self._log.warning(
|
||||
"Incorrect amount detected for coin b lock txn: {}".format(
|
||||
transfer["tx_hash"]
|
||||
)
|
||||
)
|
||||
rv = -1
|
||||
return rv
|
||||
|
||||
def findTxnByHash(self, txid):
|
||||
with self._mx_wallet:
|
||||
self.openWallet(self._wallet_filename)
|
||||
self.rpc_wallet('refresh', timeout=self._walletrpctimeoutlong)
|
||||
self.rpc_wallet("refresh", timeout=self._walletrpctimeoutlong)
|
||||
|
||||
try:
|
||||
current_height = self.rpc2('get_height', timeout=self._rpctimeout)['height']
|
||||
self._log.info(f'findTxnByHash {self.ticker_str()} current_height {current_height}\nhash: {txid}')
|
||||
current_height = self.rpc2("get_height", timeout=self._rpctimeout)[
|
||||
"height"
|
||||
]
|
||||
self._log.info(
|
||||
f"findTxnByHash {self.ticker_str()} current_height {current_height}\nhash: {txid}"
|
||||
)
|
||||
except Exception as e:
|
||||
self._log.info('rpc failed %s', str(e))
|
||||
current_height = None # If the transfer is available it will be deep enough
|
||||
self._log.info("rpc failed %s", str(e))
|
||||
current_height = (
|
||||
None # If the transfer is available it will be deep enough
|
||||
)
|
||||
|
||||
params = {'transfer_type': 'available'}
|
||||
rv = self.rpc_wallet('incoming_transfers', params)
|
||||
if 'transfers' in rv:
|
||||
for transfer in rv['transfers']:
|
||||
if transfer['tx_hash'] == txid \
|
||||
and (current_height is None or current_height - transfer['block_height'] > self.blocks_confirmed):
|
||||
return {'txid': transfer['tx_hash'], 'amount': transfer['amount'], 'height': transfer['block_height']}
|
||||
params = {"transfer_type": "available"}
|
||||
rv = self.rpc_wallet("incoming_transfers", params)
|
||||
if "transfers" in rv:
|
||||
for transfer in rv["transfers"]:
|
||||
if transfer["tx_hash"] == txid and (
|
||||
current_height is None
|
||||
or current_height - transfer["block_height"]
|
||||
> self.blocks_confirmed
|
||||
):
|
||||
return {
|
||||
"txid": transfer["tx_hash"],
|
||||
"amount": transfer["amount"],
|
||||
"height": transfer["block_height"],
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
def spendBLockTx(self, chain_b_lock_txid: bytes, address_to: str, kbv: bytes, kbs: bytes, cb_swap_value: int, b_fee_rate: int, restore_height: int, spend_actual_balance: bool = False, lock_tx_vout=None) -> bytes:
|
||||
'''
|
||||
def spendBLockTx(
|
||||
self,
|
||||
chain_b_lock_txid: bytes,
|
||||
address_to: str,
|
||||
kbv: bytes,
|
||||
kbs: bytes,
|
||||
cb_swap_value: int,
|
||||
b_fee_rate: int,
|
||||
restore_height: int,
|
||||
spend_actual_balance: bool = False,
|
||||
lock_tx_vout=None,
|
||||
) -> bytes:
|
||||
"""
|
||||
Notes:
|
||||
"Error: No unlocked balance in the specified subaddress(es)" can mean not enough funds after tx fee.
|
||||
'''
|
||||
"""
|
||||
with self._mx_wallet:
|
||||
Kbv = self.getPubkey(kbv)
|
||||
Kbs = self.getPubkey(kbs)
|
||||
address_b58 = xmr_util.encode_address(Kbv, Kbs, self._addr_prefix)
|
||||
|
||||
wallet_filename = address_b58 + '_spend'
|
||||
wallet_filename = address_b58 + "_spend"
|
||||
|
||||
params = {
|
||||
'filename': wallet_filename,
|
||||
'address': address_b58,
|
||||
'viewkey': b2h(kbv[::-1]),
|
||||
'spendkey': b2h(kbs[::-1]),
|
||||
'restore_height': restore_height,
|
||||
"filename": wallet_filename,
|
||||
"address": address_b58,
|
||||
"viewkey": b2h(kbv[::-1]),
|
||||
"spendkey": b2h(kbs[::-1]),
|
||||
"restore_height": restore_height,
|
||||
}
|
||||
|
||||
try:
|
||||
self.openWallet(wallet_filename)
|
||||
except Exception as e:
|
||||
except Exception as e: # noqa: F841
|
||||
self.createWallet(params)
|
||||
self.openWallet(wallet_filename)
|
||||
|
||||
self.rpc_wallet('refresh')
|
||||
rv = self.rpc_wallet('get_balance')
|
||||
if rv['balance'] < cb_swap_value:
|
||||
self._log.warning('Balance is too low, checking for existing spend.')
|
||||
txns = self.rpc_wallet('get_transfers', {'out': True})
|
||||
if 'out' in txns:
|
||||
txns = txns['out']
|
||||
self.rpc_wallet("refresh")
|
||||
rv = self.rpc_wallet("get_balance")
|
||||
if rv["balance"] < cb_swap_value:
|
||||
self._log.warning("Balance is too low, checking for existing spend.")
|
||||
txns = self.rpc_wallet("get_transfers", {"out": True})
|
||||
if "out" in txns:
|
||||
txns = txns["out"]
|
||||
if len(txns) > 0:
|
||||
txid = txns[0]['txid']
|
||||
self._log.warning(f'spendBLockTx detected spending tx: {txid}.')
|
||||
txid = txns[0]["txid"]
|
||||
self._log.warning(f"spendBLockTx detected spending tx: {txid}.")
|
||||
|
||||
# Should check for address_to, but only the from address is found in the output
|
||||
if txns[0]['address'] == address_b58:
|
||||
if txns[0]["address"] == address_b58:
|
||||
return bytes.fromhex(txid)
|
||||
|
||||
self._log.error('wallet {} balance {}, expected {}'.format(wallet_filename, rv['balance'], cb_swap_value))
|
||||
self._log.error(
|
||||
"wallet {} balance {}, expected {}".format(
|
||||
wallet_filename, rv["balance"], cb_swap_value
|
||||
)
|
||||
)
|
||||
|
||||
if not spend_actual_balance:
|
||||
raise TemporaryError('Invalid balance')
|
||||
raise TemporaryError("Invalid balance")
|
||||
|
||||
if spend_actual_balance and rv['balance'] != cb_swap_value:
|
||||
self._log.warning('Spending actual balance {}, not swap value {}.'.format(rv['balance'], cb_swap_value))
|
||||
cb_swap_value = rv['balance']
|
||||
if rv['unlocked_balance'] < cb_swap_value:
|
||||
self._log.error('wallet {} balance {}, expected {}, blocks_to_unlock {}'.format(wallet_filename, rv['unlocked_balance'], cb_swap_value, rv['blocks_to_unlock']))
|
||||
raise TemporaryError('Invalid unlocked_balance')
|
||||
if spend_actual_balance and rv["balance"] != cb_swap_value:
|
||||
self._log.warning(
|
||||
"Spending actual balance {}, not swap value {}.".format(
|
||||
rv["balance"], cb_swap_value
|
||||
)
|
||||
)
|
||||
cb_swap_value = rv["balance"]
|
||||
if rv["unlocked_balance"] < cb_swap_value:
|
||||
self._log.error(
|
||||
"wallet {} balance {}, expected {}, blocks_to_unlock {}".format(
|
||||
wallet_filename,
|
||||
rv["unlocked_balance"],
|
||||
cb_swap_value,
|
||||
rv["blocks_to_unlock"],
|
||||
)
|
||||
)
|
||||
raise TemporaryError("Invalid unlocked_balance")
|
||||
|
||||
params = {'address': address_to}
|
||||
params = {"address": address_to}
|
||||
if self._fee_priority > 0:
|
||||
params['priority'] = self._fee_priority
|
||||
params["priority"] = self._fee_priority
|
||||
|
||||
rv = self.rpc_wallet('sweep_all', params)
|
||||
rv = self.rpc_wallet("sweep_all", params)
|
||||
|
||||
return bytes.fromhex(rv['tx_hash_list'][0])
|
||||
return bytes.fromhex(rv["tx_hash_list"][0])
|
||||
|
||||
def withdrawCoin(self, value, addr_to: str, sweepall: bool, estimate_fee: bool = False) -> str:
|
||||
def withdrawCoin(
|
||||
self, value, addr_to: str, sweepall: bool, estimate_fee: bool = False
|
||||
) -> str:
|
||||
with self._mx_wallet:
|
||||
self.openWallet(self._wallet_filename)
|
||||
self.rpc_wallet('refresh')
|
||||
self.rpc_wallet("refresh")
|
||||
|
||||
if sweepall:
|
||||
balance = self.rpc_wallet('get_balance')
|
||||
if balance['balance'] != balance['unlocked_balance']:
|
||||
raise ValueError('Balance must be fully confirmed to use sweep all.')
|
||||
self._log.info('{} {} sweep_all.'.format(self.ticker_str(), 'estimate fee' if estimate_fee else 'withdraw'))
|
||||
self._log.debug('{} balance: {}'.format(self.ticker_str(), balance['balance']))
|
||||
params = {'address': addr_to, 'do_not_relay': estimate_fee, 'subaddr_indices_all': True}
|
||||
balance = self.rpc_wallet("get_balance")
|
||||
if balance["balance"] != balance["unlocked_balance"]:
|
||||
raise ValueError(
|
||||
"Balance must be fully confirmed to use sweep all."
|
||||
)
|
||||
self._log.info(
|
||||
"{} {} sweep_all.".format(
|
||||
self.ticker_str(),
|
||||
"estimate fee" if estimate_fee else "withdraw",
|
||||
)
|
||||
)
|
||||
self._log.debug(
|
||||
"{} balance: {}".format(self.ticker_str(), balance["balance"])
|
||||
)
|
||||
params = {
|
||||
"address": addr_to,
|
||||
"do_not_relay": estimate_fee,
|
||||
"subaddr_indices_all": True,
|
||||
}
|
||||
if self._fee_priority > 0:
|
||||
params['priority'] = self._fee_priority
|
||||
rv = self.rpc_wallet('sweep_all', params)
|
||||
params["priority"] = self._fee_priority
|
||||
rv = self.rpc_wallet("sweep_all", params)
|
||||
if estimate_fee:
|
||||
return {'num_txns': len(rv['fee_list']), 'sum_amount': sum(rv['amount_list']), 'sum_fee': sum(rv['fee_list']), 'sum_weight': sum(rv['weight_list'])}
|
||||
return rv['tx_hash_list'][0]
|
||||
return {
|
||||
"num_txns": len(rv["fee_list"]),
|
||||
"sum_amount": sum(rv["amount_list"]),
|
||||
"sum_fee": sum(rv["fee_list"]),
|
||||
"sum_weight": sum(rv["weight_list"]),
|
||||
}
|
||||
return rv["tx_hash_list"][0]
|
||||
|
||||
value_sats: int = self.make_int(value)
|
||||
params = {'destinations': [{'amount': value_sats, 'address': addr_to}], 'do_not_relay': estimate_fee}
|
||||
params = {
|
||||
"destinations": [{"amount": value_sats, "address": addr_to}],
|
||||
"do_not_relay": estimate_fee,
|
||||
}
|
||||
if self._fee_priority > 0:
|
||||
params['priority'] = self._fee_priority
|
||||
rv = self.rpc_wallet('transfer', params)
|
||||
params["priority"] = self._fee_priority
|
||||
rv = self.rpc_wallet("transfer", params)
|
||||
if estimate_fee:
|
||||
return {'num_txns': 1, 'sum_amount': rv['amount'], 'sum_fee': rv['fee'], 'sum_weight': rv['weight']}
|
||||
return rv['tx_hash']
|
||||
return {
|
||||
"num_txns": 1,
|
||||
"sum_amount": rv["amount"],
|
||||
"sum_fee": rv["fee"],
|
||||
"sum_weight": rv["weight"],
|
||||
}
|
||||
return rv["tx_hash"]
|
||||
|
||||
def estimateFee(self, value: int, addr_to: str, sweepall: bool) -> str:
|
||||
return self.withdrawCoin(value, addr_to, sweepall, estimate_fee=True)
|
||||
|
@ -512,7 +649,7 @@ class XMRInterface(CoinInterface):
|
|||
try:
|
||||
Kbv = self.getPubkey(kbv)
|
||||
address_b58 = xmr_util.encode_address(Kbv, Kbs, self._addr_prefix)
|
||||
wallet_file = address_b58 + '_spend'
|
||||
wallet_file = address_b58 + "_spend"
|
||||
try:
|
||||
self.openWallet(wallet_file)
|
||||
except Exception:
|
||||
|
@ -520,54 +657,62 @@ class XMRInterface(CoinInterface):
|
|||
try:
|
||||
self.openWallet(wallet_file)
|
||||
except Exception:
|
||||
self._log.info(f'showLockTransfers trying to create wallet for address {address_b58}.')
|
||||
self._log.info(
|
||||
f"showLockTransfers trying to create wallet for address {address_b58}."
|
||||
)
|
||||
kbv_le = kbv[::-1]
|
||||
params = {
|
||||
'restore_height': restore_height,
|
||||
'filename': address_b58,
|
||||
'address': address_b58,
|
||||
'viewkey': b2h(kbv_le),
|
||||
"restore_height": restore_height,
|
||||
"filename": address_b58,
|
||||
"address": address_b58,
|
||||
"viewkey": b2h(kbv_le),
|
||||
}
|
||||
self.createWallet(params)
|
||||
self.openWallet(address_b58)
|
||||
|
||||
self.rpc_wallet('refresh')
|
||||
self.rpc_wallet("refresh")
|
||||
|
||||
rv = self.rpc_wallet('get_transfers', {'in': True, 'out': True, 'pending': True, 'failed': True})
|
||||
rv['filename'] = wallet_file
|
||||
rv = self.rpc_wallet(
|
||||
"get_transfers",
|
||||
{"in": True, "out": True, "pending": True, "failed": True},
|
||||
)
|
||||
rv["filename"] = wallet_file
|
||||
return rv
|
||||
except Exception as e:
|
||||
return {'error': str(e)}
|
||||
return {"error": str(e)}
|
||||
|
||||
def getSpendableBalance(self) -> int:
|
||||
with self._mx_wallet:
|
||||
self.openWallet(self._wallet_filename)
|
||||
|
||||
self.rpc_wallet('refresh')
|
||||
balance_info = self.rpc_wallet('get_balance')
|
||||
return balance_info['unlocked_balance']
|
||||
self.rpc_wallet("refresh")
|
||||
balance_info = self.rpc_wallet("get_balance")
|
||||
return balance_info["unlocked_balance"]
|
||||
|
||||
def changeWalletPassword(self, old_password, new_password):
|
||||
self._log.info('changeWalletPassword - {}'.format(self.ticker()))
|
||||
self._log.info("changeWalletPassword - {}".format(self.ticker()))
|
||||
orig_password = self._wallet_password
|
||||
if old_password != '':
|
||||
if old_password != "":
|
||||
self._wallet_password = old_password
|
||||
try:
|
||||
self.openWallet(self._wallet_filename)
|
||||
self.rpc_wallet('change_wallet_password', {'old_password': old_password, 'new_password': new_password})
|
||||
self.rpc_wallet(
|
||||
"change_wallet_password",
|
||||
{"old_password": old_password, "new_password": new_password},
|
||||
)
|
||||
except Exception as e:
|
||||
self._wallet_password = orig_password
|
||||
raise e
|
||||
|
||||
def unlockWallet(self, password: str) -> None:
|
||||
self._log.info('unlockWallet - {}'.format(self.ticker()))
|
||||
self._log.info("unlockWallet - {}".format(self.ticker()))
|
||||
self._wallet_password = password
|
||||
|
||||
if not self._have_checked_seed:
|
||||
self._sc.checkWalletSeed(self.coin_type())
|
||||
|
||||
def lockWallet(self) -> None:
|
||||
self._log.info('lockWallet - {}'.format(self.ticker()))
|
||||
self._log.info("lockWallet - {}".format(self.ticker()))
|
||||
self._wallet_password = None
|
||||
|
||||
def isAddressMine(self, address):
|
||||
|
@ -576,7 +721,14 @@ class XMRInterface(CoinInterface):
|
|||
|
||||
def ensureFunds(self, amount: int) -> None:
|
||||
if self.getSpendableBalance() < amount:
|
||||
raise ValueError('Balance too low')
|
||||
raise ValueError("Balance too low")
|
||||
|
||||
def getTransaction(self, txid: bytes):
|
||||
return self.rpc2('get_transactions', {'txs_hashes': [txid.hex(), ]})
|
||||
return self.rpc2(
|
||||
"get_transactions",
|
||||
{
|
||||
"txs_hashes": [
|
||||
txid.hex(),
|
||||
]
|
||||
},
|
||||
)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -6,7 +6,7 @@
|
|||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
||||
'''
|
||||
"""
|
||||
syntax = "proto3";
|
||||
|
||||
0 VARINT int32, int64, uint32, uint64, sint32, sint64, bool, enum
|
||||
|
@ -18,12 +18,12 @@ Don't encode fields of default values.
|
|||
When decoding initialise all fields not set from data.
|
||||
|
||||
protobuf ParseFromString would reset the whole object, from_bytes won't.
|
||||
'''
|
||||
"""
|
||||
|
||||
from basicswap.util.integer import encode_varint, decode_varint
|
||||
|
||||
|
||||
class NonProtobufClass():
|
||||
class NonProtobufClass:
|
||||
def __init__(self, init_all: bool = True, **kwargs):
|
||||
for key, value in kwargs.items():
|
||||
found_field: bool = False
|
||||
|
@ -34,7 +34,7 @@ class NonProtobufClass():
|
|||
found_field = True
|
||||
break
|
||||
if found_field is False:
|
||||
raise ValueError(f'got an unexpected keyword argument \'{key}\'')
|
||||
raise ValueError(f"got an unexpected keyword argument '{key}'")
|
||||
|
||||
if init_all:
|
||||
self.init_fields()
|
||||
|
@ -53,7 +53,7 @@ class NonProtobufClass():
|
|||
else:
|
||||
setattr(self, field_name, bytes())
|
||||
else:
|
||||
raise ValueError(f'Unknown wire_type {wire_type}')
|
||||
raise ValueError(f"Unknown wire_type {wire_type}")
|
||||
|
||||
def to_bytes(self) -> bytes:
|
||||
rv = bytes()
|
||||
|
@ -74,11 +74,11 @@ class NonProtobufClass():
|
|||
continue
|
||||
rv += encode_varint(tag)
|
||||
if isinstance(field_value, str):
|
||||
field_value = field_value.encode('utf-8')
|
||||
field_value = field_value.encode("utf-8")
|
||||
rv += encode_varint(len(field_value))
|
||||
rv += field_value
|
||||
else:
|
||||
raise ValueError(f'Unknown wire_type {wire_type}')
|
||||
raise ValueError(f"Unknown wire_type {wire_type}")
|
||||
return rv
|
||||
|
||||
def from_bytes(self, b: bytes, init_all: bool = True) -> None:
|
||||
|
@ -92,7 +92,9 @@ class NonProtobufClass():
|
|||
|
||||
field_name, wire_type_expect, field_type = self._map[field_num]
|
||||
if wire_type != wire_type_expect:
|
||||
raise ValueError(f'Unexpected wire_type {wire_type} for field {field_num}')
|
||||
raise ValueError(
|
||||
f"Unexpected wire_type {wire_type} for field {field_num}"
|
||||
)
|
||||
|
||||
if wire_type == 0:
|
||||
field_value, lv = decode_varint(b, o)
|
||||
|
@ -100,12 +102,12 @@ class NonProtobufClass():
|
|||
elif wire_type == 2:
|
||||
field_len, lv = decode_varint(b, o)
|
||||
o += lv
|
||||
field_value = b[o: o + field_len]
|
||||
field_value = b[o : o + field_len]
|
||||
o += field_len
|
||||
if field_type == 1:
|
||||
field_value = field_value.decode('utf-8')
|
||||
field_value = field_value.decode("utf-8")
|
||||
else:
|
||||
raise ValueError(f'Unknown wire_type {wire_type}')
|
||||
raise ValueError(f"Unknown wire_type {wire_type}")
|
||||
|
||||
setattr(self, field_name, field_value)
|
||||
|
||||
|
@ -115,151 +117,150 @@ class NonProtobufClass():
|
|||
|
||||
class OfferMessage(NonProtobufClass):
|
||||
_map = {
|
||||
1: ('protocol_version', 0, 0),
|
||||
2: ('coin_from', 0, 0),
|
||||
3: ('coin_to', 0, 0),
|
||||
4: ('amount_from', 0, 0),
|
||||
5: ('amount_to', 0, 0),
|
||||
6: ('min_bid_amount', 0, 0),
|
||||
7: ('time_valid', 0, 0),
|
||||
8: ('lock_type', 0, 0),
|
||||
9: ('lock_value', 0, 0),
|
||||
10: ('swap_type', 0, 0),
|
||||
11: ('proof_address', 2, 1),
|
||||
12: ('proof_signature', 2, 1),
|
||||
13: ('pkhash_seller', 2, 0),
|
||||
14: ('secret_hash', 2, 0),
|
||||
15: ('fee_rate_from', 0, 0),
|
||||
16: ('fee_rate_to', 0, 0),
|
||||
17: ('amount_negotiable', 0, 2),
|
||||
18: ('rate_negotiable', 0, 2),
|
||||
19: ('proof_utxos', 2, 0),
|
||||
1: ("protocol_version", 0, 0),
|
||||
2: ("coin_from", 0, 0),
|
||||
3: ("coin_to", 0, 0),
|
||||
4: ("amount_from", 0, 0),
|
||||
5: ("amount_to", 0, 0),
|
||||
6: ("min_bid_amount", 0, 0),
|
||||
7: ("time_valid", 0, 0),
|
||||
8: ("lock_type", 0, 0),
|
||||
9: ("lock_value", 0, 0),
|
||||
10: ("swap_type", 0, 0),
|
||||
11: ("proof_address", 2, 1),
|
||||
12: ("proof_signature", 2, 1),
|
||||
13: ("pkhash_seller", 2, 0),
|
||||
14: ("secret_hash", 2, 0),
|
||||
15: ("fee_rate_from", 0, 0),
|
||||
16: ("fee_rate_to", 0, 0),
|
||||
17: ("amount_negotiable", 0, 2),
|
||||
18: ("rate_negotiable", 0, 2),
|
||||
19: ("proof_utxos", 2, 0),
|
||||
}
|
||||
|
||||
|
||||
class BidMessage(NonProtobufClass):
|
||||
_map = {
|
||||
1: ('protocol_version', 0, 0),
|
||||
2: ('offer_msg_id', 2, 0),
|
||||
3: ('time_valid', 0, 0),
|
||||
4: ('amount', 0, 0),
|
||||
5: ('amount_to', 0, 0),
|
||||
6: ('pkhash_buyer', 2, 0),
|
||||
7: ('proof_address', 2, 1),
|
||||
8: ('proof_signature', 2, 1),
|
||||
9: ('proof_utxos', 2, 0),
|
||||
10: ('pkhash_buyer_to', 2, 0),
|
||||
1: ("protocol_version", 0, 0),
|
||||
2: ("offer_msg_id", 2, 0),
|
||||
3: ("time_valid", 0, 0),
|
||||
4: ("amount", 0, 0),
|
||||
5: ("amount_to", 0, 0),
|
||||
6: ("pkhash_buyer", 2, 0),
|
||||
7: ("proof_address", 2, 1),
|
||||
8: ("proof_signature", 2, 1),
|
||||
9: ("proof_utxos", 2, 0),
|
||||
10: ("pkhash_buyer_to", 2, 0),
|
||||
}
|
||||
|
||||
|
||||
class BidAcceptMessage(NonProtobufClass):
|
||||
# Step 3, seller -> buyer
|
||||
_map = {
|
||||
1: ('bid_msg_id', 2, 0),
|
||||
2: ('initiate_txid', 2, 0),
|
||||
3: ('contract_script', 2, 0),
|
||||
4: ('pkhash_seller', 2, 0),
|
||||
1: ("bid_msg_id", 2, 0),
|
||||
2: ("initiate_txid", 2, 0),
|
||||
3: ("contract_script", 2, 0),
|
||||
4: ("pkhash_seller", 2, 0),
|
||||
}
|
||||
|
||||
|
||||
class OfferRevokeMessage(NonProtobufClass):
|
||||
_map = {
|
||||
1: ('offer_msg_id', 2, 0),
|
||||
2: ('signature', 2, 0),
|
||||
1: ("offer_msg_id", 2, 0),
|
||||
2: ("signature", 2, 0),
|
||||
}
|
||||
|
||||
|
||||
class BidRejectMessage(NonProtobufClass):
|
||||
_map = {
|
||||
1: ('bid_msg_id', 2, 0),
|
||||
2: ('reject_code', 0, 0),
|
||||
1: ("bid_msg_id", 2, 0),
|
||||
2: ("reject_code", 0, 0),
|
||||
}
|
||||
|
||||
|
||||
class XmrBidMessage(NonProtobufClass):
|
||||
# MSG1L, F -> L
|
||||
_map = {
|
||||
1: ('protocol_version', 0, 0),
|
||||
2: ('offer_msg_id', 2, 0),
|
||||
3: ('time_valid', 0, 0),
|
||||
4: ('amount', 0, 0),
|
||||
5: ('amount_to', 0, 0),
|
||||
6: ('pkaf', 2, 0),
|
||||
7: ('kbvf', 2, 0),
|
||||
8: ('kbsf_dleag', 2, 0),
|
||||
9: ('dest_af', 2, 0),
|
||||
1: ("protocol_version", 0, 0),
|
||||
2: ("offer_msg_id", 2, 0),
|
||||
3: ("time_valid", 0, 0),
|
||||
4: ("amount", 0, 0),
|
||||
5: ("amount_to", 0, 0),
|
||||
6: ("pkaf", 2, 0),
|
||||
7: ("kbvf", 2, 0),
|
||||
8: ("kbsf_dleag", 2, 0),
|
||||
9: ("dest_af", 2, 0),
|
||||
}
|
||||
|
||||
|
||||
class XmrSplitMessage(NonProtobufClass):
|
||||
_map = {
|
||||
1: ('msg_id', 2, 0),
|
||||
2: ('msg_type', 0, 0),
|
||||
3: ('sequence', 0, 0),
|
||||
4: ('dleag', 2, 0),
|
||||
1: ("msg_id", 2, 0),
|
||||
2: ("msg_type", 0, 0),
|
||||
3: ("sequence", 0, 0),
|
||||
4: ("dleag", 2, 0),
|
||||
}
|
||||
|
||||
|
||||
class XmrBidAcceptMessage(NonProtobufClass):
|
||||
_map = {
|
||||
1: ('bid_msg_id', 2, 0),
|
||||
2: ('pkal', 2, 0),
|
||||
3: ('kbvl', 2, 0),
|
||||
4: ('kbsl_dleag', 2, 0),
|
||||
|
||||
1: ("bid_msg_id", 2, 0),
|
||||
2: ("pkal", 2, 0),
|
||||
3: ("kbvl", 2, 0),
|
||||
4: ("kbsl_dleag", 2, 0),
|
||||
# MSG2F
|
||||
5: ('a_lock_tx', 2, 0),
|
||||
6: ('a_lock_tx_script', 2, 0),
|
||||
7: ('a_lock_refund_tx', 2, 0),
|
||||
8: ('a_lock_refund_tx_script', 2, 0),
|
||||
9: ('a_lock_refund_spend_tx', 2, 0),
|
||||
10: ('al_lock_refund_tx_sig', 2, 0),
|
||||
5: ("a_lock_tx", 2, 0),
|
||||
6: ("a_lock_tx_script", 2, 0),
|
||||
7: ("a_lock_refund_tx", 2, 0),
|
||||
8: ("a_lock_refund_tx_script", 2, 0),
|
||||
9: ("a_lock_refund_spend_tx", 2, 0),
|
||||
10: ("al_lock_refund_tx_sig", 2, 0),
|
||||
}
|
||||
|
||||
|
||||
class XmrBidLockTxSigsMessage(NonProtobufClass):
|
||||
# MSG3L
|
||||
_map = {
|
||||
1: ('bid_msg_id', 2, 0),
|
||||
2: ('af_lock_refund_spend_tx_esig', 2, 0),
|
||||
3: ('af_lock_refund_tx_sig', 2, 0),
|
||||
1: ("bid_msg_id", 2, 0),
|
||||
2: ("af_lock_refund_spend_tx_esig", 2, 0),
|
||||
3: ("af_lock_refund_tx_sig", 2, 0),
|
||||
}
|
||||
|
||||
|
||||
class XmrBidLockSpendTxMessage(NonProtobufClass):
|
||||
# MSG4F
|
||||
_map = {
|
||||
1: ('bid_msg_id', 2, 0),
|
||||
2: ('a_lock_spend_tx', 2, 0),
|
||||
3: ('kal_sig', 2, 0),
|
||||
1: ("bid_msg_id", 2, 0),
|
||||
2: ("a_lock_spend_tx", 2, 0),
|
||||
3: ("kal_sig", 2, 0),
|
||||
}
|
||||
|
||||
|
||||
class XmrBidLockReleaseMessage(NonProtobufClass):
|
||||
# MSG5F
|
||||
_map = {
|
||||
1: ('bid_msg_id', 2, 0),
|
||||
2: ('al_lock_spend_tx_esig', 2, 0),
|
||||
1: ("bid_msg_id", 2, 0),
|
||||
2: ("al_lock_spend_tx_esig", 2, 0),
|
||||
}
|
||||
|
||||
|
||||
class ADSBidIntentMessage(NonProtobufClass):
|
||||
# L -> F Sent from bidder, construct a reverse bid
|
||||
_map = {
|
||||
1: ('protocol_version', 0, 0),
|
||||
2: ('offer_msg_id', 2, 0),
|
||||
3: ('time_valid', 0, 0),
|
||||
4: ('amount_from', 0, 0),
|
||||
5: ('amount_to', 0, 0),
|
||||
1: ("protocol_version", 0, 0),
|
||||
2: ("offer_msg_id", 2, 0),
|
||||
3: ("time_valid", 0, 0),
|
||||
4: ("amount_from", 0, 0),
|
||||
5: ("amount_to", 0, 0),
|
||||
}
|
||||
|
||||
|
||||
class ADSBidIntentAcceptMessage(NonProtobufClass):
|
||||
# F -> L Sent from offerer, construct a reverse bid
|
||||
_map = {
|
||||
1: ('bid_msg_id', 2, 0),
|
||||
2: ('pkaf', 2, 0),
|
||||
3: ('kbvf', 2, 0),
|
||||
4: ('kbsf_dleag', 2, 0),
|
||||
5: ('dest_af', 2, 0),
|
||||
1: ("bid_msg_id", 2, 0),
|
||||
2: ("pkaf", 2, 0),
|
||||
3: ("kbvf", 2, 0),
|
||||
4: ("kbsf_dleag", 2, 0),
|
||||
5: ("dest_af", 2, 0),
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
'''
|
||||
"""
|
||||
Message 2 bytes msg_class, 4 bytes length, [ 2 bytes msg_type, payload ]
|
||||
|
||||
Handshake procedure:
|
||||
|
@ -17,7 +17,7 @@
|
|||
Both nodes are initialised
|
||||
|
||||
XChaCha20_Poly1305 mac is 16bytes
|
||||
'''
|
||||
"""
|
||||
|
||||
import time
|
||||
import queue
|
||||
|
@ -36,11 +36,12 @@ from Crypto.Cipher import ChaCha20_Poly1305 # TODO: Add to libsecp256k1/coincur
|
|||
from coincurve.keys import PrivateKey, PublicKey
|
||||
from basicswap.contrib.rfc6979 import (
|
||||
rfc6979_hmac_sha256_initialize,
|
||||
rfc6979_hmac_sha256_generate)
|
||||
rfc6979_hmac_sha256_generate,
|
||||
)
|
||||
|
||||
|
||||
START_TOKEN = 0xabcd
|
||||
MSG_START_TOKEN = START_TOKEN.to_bytes(2, 'big')
|
||||
START_TOKEN = 0xABCD
|
||||
MSG_START_TOKEN = START_TOKEN.to_bytes(2, "big")
|
||||
|
||||
MSG_MAX_SIZE = 0x200000 # 2MB
|
||||
|
||||
|
@ -63,49 +64,71 @@ class NetMessageTypes(IntEnum):
|
|||
return value in cls._value2member_map_
|
||||
|
||||
|
||||
'''
|
||||
"""
|
||||
class NetMessage:
|
||||
def __init__(self):
|
||||
self._msg_class = None # 2 bytes
|
||||
self._len = None # 4 bytes
|
||||
self._msg_type = None # 2 bytes
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
# Ensure handshake keys are not reused by including the time in the msg, mac and key hash
|
||||
# Verify timestamp is not too old
|
||||
# Add keys to db to catch concurrent attempts, records can be cleared periodically, the timestamp should catch older replay attempts
|
||||
class MsgHandshake:
|
||||
__slots__ = ('_timestamp', '_ephem_pk', '_ct', '_mac')
|
||||
__slots__ = ("_timestamp", "_ephem_pk", "_ct", "_mac")
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def encode_aad(self): # Additional Authenticated Data
|
||||
return int(NetMessageTypes.HANDSHAKE).to_bytes(2, 'big') + \
|
||||
self._timestamp.to_bytes(8, 'big') + \
|
||||
self._ephem_pk
|
||||
return (
|
||||
int(NetMessageTypes.HANDSHAKE).to_bytes(2, "big")
|
||||
+ self._timestamp.to_bytes(8, "big")
|
||||
+ self._ephem_pk
|
||||
)
|
||||
|
||||
def encode(self):
|
||||
return self.encode_aad() + self._ct + self._mac
|
||||
|
||||
def decode(self, msg_mv):
|
||||
o = 2
|
||||
self._timestamp = int.from_bytes(msg_mv[o: o + 8], 'big')
|
||||
self._timestamp = int.from_bytes(msg_mv[o : o + 8], "big")
|
||||
o += 8
|
||||
self._ephem_pk = bytes(msg_mv[o: o + 33])
|
||||
self._ephem_pk = bytes(msg_mv[o : o + 33])
|
||||
o += 33
|
||||
self._ct = bytes(msg_mv[o: -16])
|
||||
self._ct = bytes(msg_mv[o:-16])
|
||||
self._mac = bytes(msg_mv[-16:])
|
||||
|
||||
|
||||
class Peer:
|
||||
__slots__ = (
|
||||
'_mx', '_pubkey', '_address', '_socket', '_version', '_ready', '_incoming',
|
||||
'_connected_at', '_last_received_at', '_bytes_sent', '_bytes_received',
|
||||
'_receiving_length', '_receiving_buffer', '_recv_messages', '_misbehaving_score',
|
||||
'_ke', '_km', '_dir', '_sent_nonce', '_recv_nonce', '_last_handshake_at',
|
||||
'_ping_nonce', '_last_ping_at', '_last_ping_rtt')
|
||||
"_mx",
|
||||
"_pubkey",
|
||||
"_address",
|
||||
"_socket",
|
||||
"_version",
|
||||
"_ready",
|
||||
"_incoming",
|
||||
"_connected_at",
|
||||
"_last_received_at",
|
||||
"_bytes_sent",
|
||||
"_bytes_received",
|
||||
"_receiving_length",
|
||||
"_receiving_buffer",
|
||||
"_recv_messages",
|
||||
"_misbehaving_score",
|
||||
"_ke",
|
||||
"_km",
|
||||
"_dir",
|
||||
"_sent_nonce",
|
||||
"_recv_nonce",
|
||||
"_last_handshake_at",
|
||||
"_ping_nonce",
|
||||
"_last_ping_at",
|
||||
"_last_ping_rtt",
|
||||
)
|
||||
|
||||
def __init__(self, address, socket, pubkey):
|
||||
self._mx = threading.Lock()
|
||||
|
@ -141,14 +164,16 @@ def listen_thread(cls):
|
|||
max_bytes = 0x10000
|
||||
while cls._running:
|
||||
# logging.info('[rm] network loop %d', cls._running)
|
||||
readable, writable, errored = select.select(cls._read_sockets, cls._write_sockets, cls._error_sockets, timeout)
|
||||
readable, writable, errored = select.select(
|
||||
cls._read_sockets, cls._write_sockets, cls._error_sockets, timeout
|
||||
)
|
||||
cls._mx.acquire()
|
||||
try:
|
||||
disconnected_peers = []
|
||||
for s in readable:
|
||||
if s == cls._socket:
|
||||
peer_socket, address = cls._socket.accept()
|
||||
logging.info('Connection from %s', address)
|
||||
logging.info("Connection from %s", address)
|
||||
new_peer = Peer(address, peer_socket, None)
|
||||
new_peer._incoming = True
|
||||
cls._peers.append(new_peer)
|
||||
|
@ -160,12 +185,12 @@ def listen_thread(cls):
|
|||
try:
|
||||
bytes_recv = s.recv(max_bytes, socket.MSG_DONTWAIT)
|
||||
except socket.error as se:
|
||||
if se.args[0] not in (socket.EWOULDBLOCK, ):
|
||||
logging.error('Receive error %s', str(se))
|
||||
if se.args[0] not in (socket.EWOULDBLOCK,):
|
||||
logging.error("Receive error %s", str(se))
|
||||
disconnected_peers.append(peer)
|
||||
continue
|
||||
except Exception as e:
|
||||
logging.error('Receive error %s', str(e))
|
||||
logging.error("Receive error %s", str(e))
|
||||
disconnected_peers.append(peer)
|
||||
continue
|
||||
|
||||
|
@ -175,7 +200,7 @@ def listen_thread(cls):
|
|||
cls.receive_bytes(peer, bytes_recv)
|
||||
|
||||
for s in errored:
|
||||
logging.warning('Socket error')
|
||||
logging.warning("Socket error")
|
||||
|
||||
for peer in disconnected_peers:
|
||||
cls.disconnect(peer)
|
||||
|
@ -193,7 +218,9 @@ def msg_thread(cls):
|
|||
try:
|
||||
now_us = time.time_ns() // 1000
|
||||
if peer._ready is True:
|
||||
if now_us - peer._last_ping_at >= 5000000: # 5 seconds TODO: Make variable
|
||||
if (
|
||||
now_us - peer._last_ping_at >= 5000000
|
||||
): # 5 seconds TODO: Make variable
|
||||
cls.send_ping(peer)
|
||||
msg = peer._recv_messages.get(False)
|
||||
cls.process_message(peer, msg)
|
||||
|
@ -201,7 +228,7 @@ def msg_thread(cls):
|
|||
except queue.Empty:
|
||||
pass
|
||||
except Exception as e:
|
||||
logging.warning('process message error %s', str(e))
|
||||
logging.warning("process message error %s", str(e))
|
||||
if cls._sc.debug:
|
||||
logging.error(traceback.format_exc())
|
||||
|
||||
|
@ -211,9 +238,24 @@ def msg_thread(cls):
|
|||
|
||||
class Network:
|
||||
__slots__ = (
|
||||
'_p2p_host', '_p2p_port', '_network_key', '_network_pubkey',
|
||||
'_sc', '_peers', '_max_connections', '_running', '_network_thread', '_msg_thread',
|
||||
'_mx', '_socket', '_read_sockets', '_write_sockets', '_error_sockets', '_csprng', '_seen_ephem_keys')
|
||||
"_p2p_host",
|
||||
"_p2p_port",
|
||||
"_network_key",
|
||||
"_network_pubkey",
|
||||
"_sc",
|
||||
"_peers",
|
||||
"_max_connections",
|
||||
"_running",
|
||||
"_network_thread",
|
||||
"_msg_thread",
|
||||
"_mx",
|
||||
"_socket",
|
||||
"_read_sockets",
|
||||
"_write_sockets",
|
||||
"_error_sockets",
|
||||
"_csprng",
|
||||
"_seen_ephem_keys",
|
||||
)
|
||||
|
||||
def __init__(self, p2p_host, p2p_port, network_key, swap_client):
|
||||
self._p2p_host = p2p_host
|
||||
|
@ -278,7 +320,13 @@ class Network:
|
|||
self._mx.release()
|
||||
|
||||
def add_connection(self, host, port, peer_pubkey):
|
||||
self._sc.log.info('Connecting from %s to %s at %s %d', self._network_pubkey.hex(), peer_pubkey.hex(), host, port)
|
||||
self._sc.log.info(
|
||||
"Connecting from %s to %s at %s %d",
|
||||
self._network_pubkey.hex(),
|
||||
peer_pubkey.hex(),
|
||||
host,
|
||||
port,
|
||||
)
|
||||
self._mx.acquire()
|
||||
try:
|
||||
address = (host, port)
|
||||
|
@ -294,7 +342,7 @@ class Network:
|
|||
self.send_handshake(peer)
|
||||
|
||||
def disconnect(self, peer):
|
||||
self._sc.log.info('Closing peer socket %s', peer._address)
|
||||
self._sc.log.info("Closing peer socket %s", peer._address)
|
||||
self._read_sockets.pop(self._read_sockets.index(peer._socket))
|
||||
self._error_sockets.pop(self._error_sockets.index(peer._socket))
|
||||
peer.close()
|
||||
|
@ -305,7 +353,11 @@ class Network:
|
|||
|
||||
used = self._seen_ephem_keys.get(ephem_pk)
|
||||
if used:
|
||||
raise ValueError('Handshake ephem_pk reused %s peer %s', 'for' if direction == 1 else 'by', used[0])
|
||||
raise ValueError(
|
||||
"Handshake ephem_pk reused %s peer %s",
|
||||
"for" if direction == 1 else "by",
|
||||
used[0],
|
||||
)
|
||||
|
||||
self._seen_ephem_keys[ephem_pk] = (peer._address, timestamp)
|
||||
|
||||
|
@ -313,12 +365,14 @@ class Network:
|
|||
self._seen_ephem_keys.popitem(last=False)
|
||||
|
||||
def send_handshake(self, peer):
|
||||
self._sc.log.debug('send_handshake %s', peer._address)
|
||||
self._sc.log.debug("send_handshake %s", peer._address)
|
||||
peer._mx.acquire()
|
||||
try:
|
||||
# TODO: Drain peer._recv_messages
|
||||
if not peer._recv_messages.empty():
|
||||
self._sc.log.warning('send_handshake %s - Receive queue dumped.', peer._address)
|
||||
self._sc.log.warning(
|
||||
"send_handshake %s - Receive queue dumped.", peer._address
|
||||
)
|
||||
while not peer._recv_messages.empty():
|
||||
peer._recv_messages.get(False)
|
||||
|
||||
|
@ -332,7 +386,7 @@ class Network:
|
|||
|
||||
ss = k.ecdh(peer._pubkey)
|
||||
|
||||
hashed = hashlib.sha512(ss + msg._timestamp.to_bytes(8, 'big')).digest()
|
||||
hashed = hashlib.sha512(ss + msg._timestamp.to_bytes(8, "big")).digest()
|
||||
peer._ke = hashed[:32]
|
||||
peer._km = hashed[32:]
|
||||
|
||||
|
@ -361,11 +415,13 @@ class Network:
|
|||
peer._mx.release()
|
||||
|
||||
def process_handshake(self, peer, msg_mv):
|
||||
self._sc.log.debug('process_handshake %s', peer._address)
|
||||
self._sc.log.debug("process_handshake %s", peer._address)
|
||||
|
||||
# TODO: Drain peer._recv_messages
|
||||
if not peer._recv_messages.empty():
|
||||
self._sc.log.warning('process_handshake %s - Receive queue dumped.', peer._address)
|
||||
self._sc.log.warning(
|
||||
"process_handshake %s - Receive queue dumped.", peer._address
|
||||
)
|
||||
while not peer._recv_messages.empty():
|
||||
peer._recv_messages.get(False)
|
||||
|
||||
|
@ -375,17 +431,19 @@ class Network:
|
|||
try:
|
||||
now = int(time.time())
|
||||
if now - peer._last_handshake_at < 30:
|
||||
raise ValueError('Too many handshakes from peer %s', peer._address)
|
||||
raise ValueError("Too many handshakes from peer %s", peer._address)
|
||||
|
||||
if abs(msg._timestamp - now) > TIMESTAMP_LEEWAY:
|
||||
raise ValueError('Bad handshake timestamp from peer %s', peer._address)
|
||||
raise ValueError("Bad handshake timestamp from peer %s", peer._address)
|
||||
|
||||
self.check_handshake_ephem_key(peer, msg._timestamp, msg._ephem_pk, direction=2)
|
||||
self.check_handshake_ephem_key(
|
||||
peer, msg._timestamp, msg._ephem_pk, direction=2
|
||||
)
|
||||
|
||||
nk = PrivateKey(self._network_key)
|
||||
ss = nk.ecdh(msg._ephem_pk)
|
||||
|
||||
hashed = hashlib.sha512(ss + msg._timestamp.to_bytes(8, 'big')).digest()
|
||||
hashed = hashlib.sha512(ss + msg._timestamp.to_bytes(8, "big")).digest()
|
||||
peer._ke = hashed[:32]
|
||||
peer._km = hashed[32:]
|
||||
|
||||
|
@ -395,7 +453,9 @@ class Network:
|
|||
aad += nonce
|
||||
cipher = ChaCha20_Poly1305.new(key=peer._ke, nonce=nonce)
|
||||
cipher.update(aad)
|
||||
plaintext = cipher.decrypt_and_verify(msg._ct, msg._mac) # Will raise error if mac doesn't match
|
||||
plaintext = cipher.decrypt_and_verify(
|
||||
msg._ct, msg._mac
|
||||
) # Will raise error if mac doesn't match
|
||||
|
||||
peer._version = plaintext[:6]
|
||||
sig = plaintext[6:]
|
||||
|
@ -414,26 +474,30 @@ class Network:
|
|||
|
||||
except Exception as e:
|
||||
# TODO: misbehaving
|
||||
self._sc.log.debug('[rm] process_handshake %s', str(e))
|
||||
self._sc.log.debug("[rm] process_handshake %s", str(e))
|
||||
|
||||
def process_ping(self, peer, msg_mv):
|
||||
nonce = peer._recv_nonce[:24]
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=peer._ke, nonce=nonce)
|
||||
cipher.update(msg_mv[0: 2])
|
||||
cipher.update(msg_mv[0:2])
|
||||
cipher.update(nonce)
|
||||
|
||||
mac = msg_mv[-16:]
|
||||
plaintext = cipher.decrypt_and_verify(msg_mv[2: -16], mac)
|
||||
plaintext = cipher.decrypt_and_verify(msg_mv[2:-16], mac)
|
||||
|
||||
ping_nonce = int.from_bytes(plaintext[:4], 'big')
|
||||
ping_nonce = int.from_bytes(plaintext[:4], "big")
|
||||
# Version is added to a ping following a handshake message
|
||||
if len(plaintext) >= 10:
|
||||
peer._ready = True
|
||||
version = plaintext[4: 10]
|
||||
version = plaintext[4:10]
|
||||
if peer._version is None:
|
||||
peer._version = version
|
||||
self._sc.log.debug('Set version from ping %s, %s', peer._pubkey.hex(), peer._version.hex())
|
||||
self._sc.log.debug(
|
||||
"Set version from ping %s, %s",
|
||||
peer._pubkey.hex(),
|
||||
peer._version.hex(),
|
||||
)
|
||||
|
||||
peer._recv_nonce = hashlib.sha256(nonce + mac).digest()
|
||||
|
||||
|
@ -443,32 +507,32 @@ class Network:
|
|||
nonce = peer._recv_nonce[:24]
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=peer._ke, nonce=nonce)
|
||||
cipher.update(msg_mv[0: 2])
|
||||
cipher.update(msg_mv[0:2])
|
||||
cipher.update(nonce)
|
||||
|
||||
mac = msg_mv[-16:]
|
||||
plaintext = cipher.decrypt_and_verify(msg_mv[2: -16], mac)
|
||||
plaintext = cipher.decrypt_and_verify(msg_mv[2:-16], mac)
|
||||
|
||||
pong_nonce = int.from_bytes(plaintext[:4], 'big')
|
||||
pong_nonce = int.from_bytes(plaintext[:4], "big")
|
||||
|
||||
if pong_nonce == peer._ping_nonce:
|
||||
peer._last_ping_rtt = (time.time_ns() // 1000) - peer._last_ping_at
|
||||
else:
|
||||
self._sc.log.debug('Pong received out of order %s', peer._address)
|
||||
self._sc.log.debug("Pong received out of order %s", peer._address)
|
||||
|
||||
peer._recv_nonce = hashlib.sha256(nonce + mac).digest()
|
||||
|
||||
def send_ping(self, peer):
|
||||
ping_nonce = random.getrandbits(32)
|
||||
|
||||
msg_bytes = int(NetMessageTypes.PING).to_bytes(2, 'big')
|
||||
msg_bytes = int(NetMessageTypes.PING).to_bytes(2, "big")
|
||||
nonce = peer._sent_nonce[:24]
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=peer._ke, nonce=nonce)
|
||||
cipher.update(msg_bytes)
|
||||
cipher.update(nonce)
|
||||
|
||||
payload = ping_nonce.to_bytes(4, 'big')
|
||||
payload = ping_nonce.to_bytes(4, "big")
|
||||
if peer._last_ping_at == 0:
|
||||
payload += self._sc._version
|
||||
ct, mac = cipher.encrypt_and_digest(payload)
|
||||
|
@ -483,14 +547,14 @@ class Network:
|
|||
self.send_msg(peer, msg_bytes)
|
||||
|
||||
def send_pong(self, peer, ping_nonce):
|
||||
msg_bytes = int(NetMessageTypes.PONG).to_bytes(2, 'big')
|
||||
msg_bytes = int(NetMessageTypes.PONG).to_bytes(2, "big")
|
||||
nonce = peer._sent_nonce[:24]
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=peer._ke, nonce=nonce)
|
||||
cipher.update(msg_bytes)
|
||||
cipher.update(nonce)
|
||||
|
||||
payload = ping_nonce.to_bytes(4, 'big')
|
||||
payload = ping_nonce.to_bytes(4, "big")
|
||||
ct, mac = cipher.encrypt_and_digest(payload)
|
||||
msg_bytes += ct + mac
|
||||
|
||||
|
@ -502,19 +566,21 @@ class Network:
|
|||
msg_encoded = msg if isinstance(msg, bytes) else msg.encode()
|
||||
len_encoded = len(msg_encoded)
|
||||
|
||||
msg_packed = bytearray(MSG_START_TOKEN) + len_encoded.to_bytes(4, 'big') + msg_encoded
|
||||
msg_packed = (
|
||||
bytearray(MSG_START_TOKEN) + len_encoded.to_bytes(4, "big") + msg_encoded
|
||||
)
|
||||
peer._socket.sendall(msg_packed)
|
||||
|
||||
peer._bytes_sent += len_encoded
|
||||
|
||||
def process_message(self, peer, msg_bytes):
|
||||
logging.info('[rm] process_message %s len %d', peer._address, len(msg_bytes))
|
||||
logging.info("[rm] process_message %s len %d", peer._address, len(msg_bytes))
|
||||
|
||||
peer._mx.acquire()
|
||||
try:
|
||||
mv = memoryview(msg_bytes)
|
||||
o = 0
|
||||
msg_type = int.from_bytes(mv[o: o + 2], 'big')
|
||||
msg_type = int.from_bytes(mv[o : o + 2], "big")
|
||||
if msg_type == NetMessageTypes.HANDSHAKE:
|
||||
self.process_handshake(peer, mv)
|
||||
elif msg_type == NetMessageTypes.PING:
|
||||
|
@ -522,7 +588,7 @@ class Network:
|
|||
elif msg_type == NetMessageTypes.PONG:
|
||||
self.process_pong(peer, mv)
|
||||
else:
|
||||
self._sc.log.debug('Unknown message type %d', msg_type)
|
||||
self._sc.log.debug("Unknown message type %d", msg_type)
|
||||
finally:
|
||||
peer._mx.release()
|
||||
|
||||
|
@ -533,7 +599,6 @@ class Network:
|
|||
peer._last_received_at = time.time()
|
||||
peer._bytes_received += len_received
|
||||
|
||||
invalid_msg = False
|
||||
mv = memoryview(bytes_recv)
|
||||
|
||||
o = 0
|
||||
|
@ -541,34 +606,34 @@ class Network:
|
|||
while o < len_received:
|
||||
if peer._receiving_length == 0:
|
||||
if len(bytes_recv) < MSG_HEADER_LEN:
|
||||
raise ValueError('Msg too short')
|
||||
raise ValueError("Msg too short")
|
||||
|
||||
if mv[o: o + 2] != MSG_START_TOKEN:
|
||||
raise ValueError('Invalid start token')
|
||||
if mv[o : o + 2] != MSG_START_TOKEN:
|
||||
raise ValueError("Invalid start token")
|
||||
o += 2
|
||||
|
||||
msg_len = int.from_bytes(mv[o: o + 4], 'big')
|
||||
msg_len = int.from_bytes(mv[o : o + 4], "big")
|
||||
o += 4
|
||||
if msg_len < 2 or msg_len > MSG_MAX_SIZE:
|
||||
raise ValueError('Invalid data length')
|
||||
raise ValueError("Invalid data length")
|
||||
|
||||
# Precheck msg_type
|
||||
msg_type = int.from_bytes(mv[o: o + 2], 'big')
|
||||
msg_type = int.from_bytes(mv[o : o + 2], "big")
|
||||
# o += 2 # Don't inc offset, msg includes type
|
||||
if not NetMessageTypes.has_value(msg_type):
|
||||
raise ValueError('Invalid msg type')
|
||||
raise ValueError("Invalid msg type")
|
||||
|
||||
peer._receiving_length = msg_len
|
||||
len_pkt = (len_received - o)
|
||||
len_pkt = len_received - o
|
||||
nc = msg_len if len_pkt > msg_len else len_pkt
|
||||
|
||||
peer._receiving_buffer = mv[o: o + nc]
|
||||
peer._receiving_buffer = mv[o : o + nc]
|
||||
o += nc
|
||||
else:
|
||||
len_to_go = peer._receiving_length - len(peer._receiving_buffer)
|
||||
len_pkt = (len_received - o)
|
||||
len_pkt = len_received - o
|
||||
nc = len_to_go if len_pkt > len_to_go else len_pkt
|
||||
peer._receiving_buffer = mv[o: o + nc]
|
||||
peer._receiving_buffer = mv[o : o + nc]
|
||||
o += nc
|
||||
if len(peer._receiving_buffer) == peer._receiving_length:
|
||||
peer._recv_messages.put(peer._receiving_buffer)
|
||||
|
@ -576,11 +641,13 @@ class Network:
|
|||
|
||||
except Exception as e:
|
||||
if self._sc.debug:
|
||||
self._sc.log.error('Invalid message received from %s %s', peer._address, str(e))
|
||||
self._sc.log.error(
|
||||
"Invalid message received from %s %s", peer._address, str(e)
|
||||
)
|
||||
# TODO: misbehaving
|
||||
|
||||
def test_onion(self, path):
|
||||
self._sc.log.debug('test_onion packet')
|
||||
self._sc.log.debug("test_onion packet")
|
||||
|
||||
def get_info(self):
|
||||
rv = {}
|
||||
|
@ -589,14 +656,14 @@ class Network:
|
|||
with self._mx:
|
||||
for peer in self._peers:
|
||||
peer_info = {
|
||||
'pubkey': 'Unknown' if not peer._pubkey else peer._pubkey.hex(),
|
||||
'address': '{}:{}'.format(peer._address[0], peer._address[1]),
|
||||
'bytessent': peer._bytes_sent,
|
||||
'bytesrecv': peer._bytes_received,
|
||||
'ready': peer._ready,
|
||||
'incoming': peer._incoming,
|
||||
"pubkey": "Unknown" if not peer._pubkey else peer._pubkey.hex(),
|
||||
"address": "{}:{}".format(peer._address[0], peer._address[1]),
|
||||
"bytessent": peer._bytes_sent,
|
||||
"bytesrecv": peer._bytes_received,
|
||||
"ready": peer._ready,
|
||||
"incoming": peer._incoming,
|
||||
}
|
||||
peers.append(peer_info)
|
||||
|
||||
rv['peers'] = peers
|
||||
rv["peers"] = peers
|
||||
return rv
|
||||
|
|
|
@ -16,19 +16,26 @@ class ProtocolInterface:
|
|||
swap_type = None
|
||||
|
||||
def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes:
|
||||
raise ValueError('base class')
|
||||
raise ValueError("base class")
|
||||
|
||||
def getMockScript(self) -> bytearray:
|
||||
return bytearray([
|
||||
OpCodes.OP_RETURN, OpCodes.OP_1])
|
||||
return bytearray([OpCodes.OP_RETURN, OpCodes.OP_1])
|
||||
|
||||
def getMockScriptScriptPubkey(self, ci) -> bytearray:
|
||||
script = self.getMockScript()
|
||||
return ci.getScriptDest(script) if ci._use_segwit else ci.get_p2sh_script_pubkey(script)
|
||||
return (
|
||||
ci.getScriptDest(script)
|
||||
if ci._use_segwit
|
||||
else ci.get_p2sh_script_pubkey(script)
|
||||
)
|
||||
|
||||
def getMockAddrTo(self, ci):
|
||||
script = self.getMockScript()
|
||||
return ci.encodeScriptDest(ci.getScriptDest(script)) if ci._use_segwit else ci.encode_p2sh(script)
|
||||
return (
|
||||
ci.encodeScriptDest(ci.getScriptDest(script))
|
||||
if ci._use_segwit
|
||||
else ci.encode_p2sh(script)
|
||||
)
|
||||
|
||||
def findMockVout(self, ci, itx_decoded):
|
||||
mock_addr = self.getMockAddrTo(ci)
|
||||
|
|
|
@ -26,73 +26,91 @@ INITIATE_TX_TIMEOUT = 40 * 60 # TODO: make variable per coin
|
|||
ABS_LOCK_TIME_LEEWAY = 10 * 60
|
||||
|
||||
|
||||
def buildContractScript(lock_val: int, secret_hash: bytes, pkh_redeem: bytes, pkh_refund: bytes, op_lock=OpCodes.OP_CHECKSEQUENCEVERIFY, op_hash=OpCodes.OP_SHA256) -> bytearray:
|
||||
script = bytearray([
|
||||
OpCodes.OP_IF,
|
||||
OpCodes.OP_SIZE,
|
||||
0x01, 0x20, # 32
|
||||
OpCodes.OP_EQUALVERIFY,
|
||||
op_hash,
|
||||
0x20]) \
|
||||
+ secret_hash \
|
||||
+ bytearray([
|
||||
OpCodes.OP_EQUALVERIFY,
|
||||
OpCodes.OP_DUP,
|
||||
OpCodes.OP_HASH160,
|
||||
0x14]) \
|
||||
+ pkh_redeem \
|
||||
+ bytearray([OpCodes.OP_ELSE, ]) \
|
||||
+ SerialiseNum(lock_val) \
|
||||
+ bytearray([
|
||||
op_lock,
|
||||
OpCodes.OP_DROP,
|
||||
OpCodes.OP_DUP,
|
||||
OpCodes.OP_HASH160,
|
||||
0x14]) \
|
||||
+ pkh_refund \
|
||||
+ bytearray([
|
||||
OpCodes.OP_ENDIF,
|
||||
OpCodes.OP_EQUALVERIFY,
|
||||
OpCodes.OP_CHECKSIG])
|
||||
def buildContractScript(
|
||||
lock_val: int,
|
||||
secret_hash: bytes,
|
||||
pkh_redeem: bytes,
|
||||
pkh_refund: bytes,
|
||||
op_lock=OpCodes.OP_CHECKSEQUENCEVERIFY,
|
||||
op_hash=OpCodes.OP_SHA256,
|
||||
) -> bytearray:
|
||||
script = (
|
||||
bytearray(
|
||||
[
|
||||
OpCodes.OP_IF,
|
||||
OpCodes.OP_SIZE,
|
||||
0x01,
|
||||
0x20, # 32
|
||||
OpCodes.OP_EQUALVERIFY,
|
||||
op_hash,
|
||||
0x20,
|
||||
]
|
||||
)
|
||||
+ secret_hash
|
||||
+ bytearray([OpCodes.OP_EQUALVERIFY, OpCodes.OP_DUP, OpCodes.OP_HASH160, 0x14])
|
||||
+ pkh_redeem
|
||||
+ bytearray(
|
||||
[
|
||||
OpCodes.OP_ELSE,
|
||||
]
|
||||
)
|
||||
+ SerialiseNum(lock_val)
|
||||
+ bytearray(
|
||||
[op_lock, OpCodes.OP_DROP, OpCodes.OP_DUP, OpCodes.OP_HASH160, 0x14]
|
||||
)
|
||||
+ pkh_refund
|
||||
+ bytearray([OpCodes.OP_ENDIF, OpCodes.OP_EQUALVERIFY, OpCodes.OP_CHECKSIG])
|
||||
)
|
||||
return script
|
||||
|
||||
|
||||
def verifyContractScript(script, op_lock=OpCodes.OP_CHECKSEQUENCEVERIFY, op_hash=OpCodes.OP_SHA256):
|
||||
if script[0] != OpCodes.OP_IF or \
|
||||
script[1] != OpCodes.OP_SIZE or \
|
||||
script[2] != 0x01 or script[3] != 0x20 or \
|
||||
script[4] != OpCodes.OP_EQUALVERIFY or \
|
||||
script[5] != op_hash or \
|
||||
script[6] != 0x20:
|
||||
def verifyContractScript(
|
||||
script, op_lock=OpCodes.OP_CHECKSEQUENCEVERIFY, op_hash=OpCodes.OP_SHA256
|
||||
):
|
||||
if (
|
||||
script[0] != OpCodes.OP_IF
|
||||
or script[1] != OpCodes.OP_SIZE
|
||||
or script[2] != 0x01
|
||||
or script[3] != 0x20
|
||||
or script[4] != OpCodes.OP_EQUALVERIFY
|
||||
or script[5] != op_hash
|
||||
or script[6] != 0x20
|
||||
):
|
||||
return False, None, None, None, None
|
||||
o = 7
|
||||
script_hash = script[o: o + 32]
|
||||
script_hash = script[o : o + 32]
|
||||
o += 32
|
||||
if script[o] != OpCodes.OP_EQUALVERIFY or \
|
||||
script[o + 1] != OpCodes.OP_DUP or \
|
||||
script[o + 2] != OpCodes.OP_HASH160 or \
|
||||
script[o + 3] != 0x14:
|
||||
if (
|
||||
script[o] != OpCodes.OP_EQUALVERIFY
|
||||
or script[o + 1] != OpCodes.OP_DUP
|
||||
or script[o + 2] != OpCodes.OP_HASH160
|
||||
or script[o + 3] != 0x14
|
||||
):
|
||||
return False, script_hash, None, None, None
|
||||
o += 4
|
||||
pkh_redeem = script[o: o + 20]
|
||||
pkh_redeem = script[o : o + 20]
|
||||
o += 20
|
||||
if script[o] != OpCodes.OP_ELSE:
|
||||
return False, script_hash, pkh_redeem, None, None
|
||||
o += 1
|
||||
lock_val, nb = decodeScriptNum(script, o)
|
||||
o += nb
|
||||
if script[o] != op_lock or \
|
||||
script[o + 1] != OpCodes.OP_DROP or \
|
||||
script[o + 2] != OpCodes.OP_DUP or \
|
||||
script[o + 3] != OpCodes.OP_HASH160 or \
|
||||
script[o + 4] != 0x14:
|
||||
if (
|
||||
script[o] != op_lock
|
||||
or script[o + 1] != OpCodes.OP_DROP
|
||||
or script[o + 2] != OpCodes.OP_DUP
|
||||
or script[o + 3] != OpCodes.OP_HASH160
|
||||
or script[o + 4] != 0x14
|
||||
):
|
||||
return False, script_hash, pkh_redeem, lock_val, None
|
||||
o += 5
|
||||
pkh_refund = script[o: o + 20]
|
||||
pkh_refund = script[o : o + 20]
|
||||
o += 20
|
||||
if script[o] != OpCodes.OP_ENDIF or \
|
||||
script[o + 1] != OpCodes.OP_EQUALVERIFY or \
|
||||
script[o + 2] != OpCodes.OP_CHECKSIG:
|
||||
if (
|
||||
script[o] != OpCodes.OP_ENDIF
|
||||
or script[o + 1] != OpCodes.OP_EQUALVERIFY
|
||||
or script[o + 2] != OpCodes.OP_CHECKSIG
|
||||
):
|
||||
return False, script_hash, pkh_redeem, lock_val, pkh_refund
|
||||
return True, script_hash, pkh_redeem, lock_val, pkh_refund
|
||||
|
||||
|
@ -105,12 +123,19 @@ def redeemITx(self, bid_id: bytes, session):
|
|||
bid, offer = self.getBidAndOffer(bid_id, session)
|
||||
ci_from = self.ci(offer.coin_from)
|
||||
|
||||
txn = self.createRedeemTxn(ci_from.coin_type(), bid, for_txn_type='initiate', session=session)
|
||||
txn = self.createRedeemTxn(
|
||||
ci_from.coin_type(), bid, for_txn_type="initiate", session=session
|
||||
)
|
||||
txid = ci_from.publishTx(bytes.fromhex(txn))
|
||||
|
||||
bid.initiate_tx.spend_txid = bytes.fromhex(txid)
|
||||
self.log.debug('Submitted initiate redeem txn %s to %s chain for bid %s', txid, ci_from.coin_name(), bid_id.hex())
|
||||
self.logEvent(Concepts.BID, bid_id, EventLogTypes.ITX_REDEEM_PUBLISHED, '', session)
|
||||
self.log.debug(
|
||||
"Submitted initiate redeem txn %s to %s chain for bid %s",
|
||||
txid,
|
||||
ci_from.coin_name(),
|
||||
bid_id.hex(),
|
||||
)
|
||||
self.logEvent(Concepts.BID, bid_id, EventLogTypes.ITX_REDEEM_PUBLISHED, "", session)
|
||||
|
||||
|
||||
class AtomicSwapInterface(ProtocolInterface):
|
||||
|
@ -118,13 +143,19 @@ class AtomicSwapInterface(ProtocolInterface):
|
|||
|
||||
def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes:
|
||||
addr_to = self.getMockAddrTo(ci)
|
||||
funded_tx = ci.createRawFundedTransaction(addr_to, amount, sub_fee, lock_unspents=False)
|
||||
funded_tx = ci.createRawFundedTransaction(
|
||||
addr_to, amount, sub_fee, lock_unspents=False
|
||||
)
|
||||
|
||||
return bytes.fromhex(funded_tx)
|
||||
|
||||
def promoteMockTx(self, ci, mock_tx: bytes, script: bytearray) -> bytearray:
|
||||
mock_txo_script = self.getMockScriptScriptPubkey(ci)
|
||||
real_txo_script = ci.getScriptDest(script) if ci._use_segwit else ci.get_p2sh_script_pubkey(script)
|
||||
real_txo_script = (
|
||||
ci.getScriptDest(script)
|
||||
if ci._use_segwit
|
||||
else ci.get_p2sh_script_pubkey(script)
|
||||
)
|
||||
|
||||
found: int = 0
|
||||
ctx = ci.loadTx(mock_tx)
|
||||
|
@ -134,9 +165,9 @@ class AtomicSwapInterface(ProtocolInterface):
|
|||
found += 1
|
||||
|
||||
if found < 1:
|
||||
raise ValueError('Mocked output not found')
|
||||
raise ValueError("Mocked output not found")
|
||||
if found > 1:
|
||||
raise ValueError('Too many mocked outputs found')
|
||||
raise ValueError("Too many mocked outputs found")
|
||||
ctx.nLockTime = 0
|
||||
|
||||
funded_tx = ctx.serialize()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020-2024 tecnovert
|
||||
# Copyright (c) 2024 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -19,18 +20,17 @@ from basicswap.basicswap_util import (
|
|||
EventLogTypes,
|
||||
)
|
||||
from . import ProtocolInterface
|
||||
from basicswap.contrib.test_framework.script import (
|
||||
CScript, CScriptOp,
|
||||
OP_CHECKMULTISIG
|
||||
)
|
||||
from basicswap.contrib.test_framework.script import CScript, CScriptOp, OP_CHECKMULTISIG
|
||||
|
||||
|
||||
def addLockRefundSigs(self, xmr_swap, ci):
|
||||
self.log.debug('Setting lock refund tx sigs')
|
||||
self.log.debug("Setting lock refund tx sigs")
|
||||
|
||||
witness_stack = []
|
||||
if ci.coin_type() not in (Coins.DCR, ):
|
||||
witness_stack += [b'', ]
|
||||
if ci.coin_type() not in (Coins.DCR,):
|
||||
witness_stack += [
|
||||
b"",
|
||||
]
|
||||
witness_stack += [
|
||||
xmr_swap.al_lock_refund_tx_sig,
|
||||
xmr_swap.af_lock_refund_tx_sig,
|
||||
|
@ -38,37 +38,40 @@ def addLockRefundSigs(self, xmr_swap, ci):
|
|||
]
|
||||
|
||||
signed_tx = ci.setTxSignature(xmr_swap.a_lock_refund_tx, witness_stack)
|
||||
ensure(signed_tx, 'setTxSignature failed')
|
||||
ensure(signed_tx, "setTxSignature failed")
|
||||
xmr_swap.a_lock_refund_tx = signed_tx
|
||||
|
||||
|
||||
def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, session=None):
|
||||
self.log.info(f'Manually recovering {bid_id.hex()}')
|
||||
self.log.info(f"Manually recovering {bid_id.hex()}")
|
||||
# Manually recover txn if other key is known
|
||||
try:
|
||||
use_session = self.openSession(session)
|
||||
bid, xmr_swap = self.getXmrBidFromSession(use_session, bid_id)
|
||||
ensure(bid, 'Bid not found: {}.'.format(bid_id.hex()))
|
||||
ensure(xmr_swap, 'Adaptor-sig swap not found: {}.'.format(bid_id.hex()))
|
||||
offer, xmr_offer = self.getXmrOfferFromSession(use_session, bid.offer_id, sent=False)
|
||||
ensure(offer, 'Offer not found: {}.'.format(bid.offer_id.hex()))
|
||||
ensure(xmr_offer, 'Adaptor-sig offer not found: {}.'.format(bid.offer_id.hex()))
|
||||
ensure(bid, "Bid not found: {}.".format(bid_id.hex()))
|
||||
ensure(xmr_swap, "Adaptor-sig swap not found: {}.".format(bid_id.hex()))
|
||||
offer, xmr_offer = self.getXmrOfferFromSession(
|
||||
use_session, bid.offer_id, sent=False
|
||||
)
|
||||
ensure(offer, "Offer not found: {}.".format(bid.offer_id.hex()))
|
||||
ensure(xmr_offer, "Adaptor-sig offer not found: {}.".format(bid.offer_id.hex()))
|
||||
|
||||
# The no-script coin is always the follower
|
||||
reverse_bid: bool = self.is_reverse_ads_bid(offer.coin_from)
|
||||
ci_from = self.ci(Coins(offer.coin_from))
|
||||
ci_to = self.ci(Coins(offer.coin_to))
|
||||
ci_leader = ci_to if reverse_bid else ci_from
|
||||
ci_follower = ci_from if reverse_bid else ci_to
|
||||
|
||||
try:
|
||||
decoded_key_half = ci_follower.decodeKey(encoded_key)
|
||||
except Exception as e:
|
||||
raise ValueError('Failed to decode provided key-half: ', str(e))
|
||||
raise ValueError("Failed to decode provided key-half: ", str(e))
|
||||
|
||||
was_sent: bool = bid.was_received if reverse_bid else bid.was_sent
|
||||
|
||||
localkeyhalf = ci_follower.decodeKey(getChainBSplitKey(self, bid, xmr_swap, offer))
|
||||
localkeyhalf = ci_follower.decodeKey(
|
||||
getChainBSplitKey(self, bid, xmr_swap, offer)
|
||||
)
|
||||
if was_sent:
|
||||
kbsl = decoded_key_half
|
||||
kbsf = localkeyhalf
|
||||
|
@ -76,32 +79,54 @@ def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, session=None):
|
|||
kbsl = localkeyhalf
|
||||
kbsf = decoded_key_half
|
||||
|
||||
ensure(ci_follower.verifyKey(kbsl), 'Invalid kbsl')
|
||||
ensure(ci_follower.verifyKey(kbsf), 'Invalid kbsf')
|
||||
ensure(ci_follower.verifyKey(kbsl), "Invalid kbsl")
|
||||
ensure(ci_follower.verifyKey(kbsf), "Invalid kbsf")
|
||||
if kbsl == kbsf:
|
||||
raise ValueError('Provided key matches local key')
|
||||
raise ValueError("Provided key matches local key")
|
||||
vkbs = ci_follower.sumKeys(kbsl, kbsf)
|
||||
|
||||
ensure(ci_follower.verifyPubkey(xmr_swap.pkbs), 'Invalid pkbs') # Sanity check
|
||||
ensure(ci_follower.verifyPubkey(xmr_swap.pkbs), "Invalid pkbs") # Sanity check
|
||||
|
||||
# Ensure summed key matches the expected pubkey
|
||||
summed_pkbs = ci_follower.getPubkey(vkbs)
|
||||
if (summed_pkbs != xmr_swap.pkbs):
|
||||
err_msg: str = 'Summed key does not match expected wallet spend pubkey'
|
||||
if summed_pkbs != xmr_swap.pkbs:
|
||||
err_msg: str = "Summed key does not match expected wallet spend pubkey"
|
||||
have_pk = summed_pkbs.hex()
|
||||
expect_pk = xmr_swap.pkbs.hex()
|
||||
self.log.error(f'{err_msg}. Got: {have_pk}, Expect: {expect_pk}')
|
||||
self.log.error(f"{err_msg}. Got: {have_pk}, Expect: {expect_pk}")
|
||||
raise ValueError(err_msg)
|
||||
|
||||
if ci_follower.coin_type() in (Coins.XMR, Coins.WOW):
|
||||
address_to = self.getCachedMainWalletAddress(ci_follower, use_session)
|
||||
else:
|
||||
address_to = self.getCachedStealthAddressForCoin(ci_follower.coin_type(), use_session)
|
||||
address_to = self.getCachedStealthAddressForCoin(
|
||||
ci_follower.coin_type(), use_session
|
||||
)
|
||||
amount = bid.amount_to
|
||||
lock_tx_vout = bid.getLockTXBVout()
|
||||
txid = ci_follower.spendBLockTx(xmr_swap.b_lock_tx_id, address_to, xmr_swap.vkbv, vkbs, amount, xmr_offer.b_fee_rate, bid.chain_b_height_start, spend_actual_balance=True, lock_tx_vout=lock_tx_vout)
|
||||
self.log.debug('Submitted lock B spend txn %s to %s chain for bid %s', txid.hex(), ci_follower.coin_name(), bid_id.hex())
|
||||
self.logBidEvent(bid.bid_id, EventLogTypes.LOCK_TX_B_SPEND_TX_PUBLISHED, txid.hex(), use_session)
|
||||
txid = ci_follower.spendBLockTx(
|
||||
xmr_swap.b_lock_tx_id,
|
||||
address_to,
|
||||
xmr_swap.vkbv,
|
||||
vkbs,
|
||||
amount,
|
||||
xmr_offer.b_fee_rate,
|
||||
bid.chain_b_height_start,
|
||||
spend_actual_balance=True,
|
||||
lock_tx_vout=lock_tx_vout,
|
||||
)
|
||||
self.log.debug(
|
||||
"Submitted lock B spend txn %s to %s chain for bid %s",
|
||||
txid.hex(),
|
||||
ci_follower.coin_name(),
|
||||
bid_id.hex(),
|
||||
)
|
||||
self.logBidEvent(
|
||||
bid.bid_id,
|
||||
EventLogTypes.LOCK_TX_B_SPEND_TX_PUBLISHED,
|
||||
txid.hex(),
|
||||
use_session,
|
||||
)
|
||||
use_session.commit()
|
||||
|
||||
return txid
|
||||
|
@ -122,7 +147,16 @@ def getChainBSplitKey(swap_client, bid, xmr_swap, offer):
|
|||
was_sent: bool = bid.was_received if reverse_bid else bid.was_sent
|
||||
|
||||
key_type = KeyTypes.KBSF if was_sent else KeyTypes.KBSL
|
||||
return ci_follower.encodeKey(swap_client.getPathKey(ci_leader.coin_type(), ci_follower.coin_type(), bid.created_at, xmr_swap.contract_count, key_type, for_ed25519))
|
||||
return ci_follower.encodeKey(
|
||||
swap_client.getPathKey(
|
||||
ci_leader.coin_type(),
|
||||
ci_follower.coin_type(),
|
||||
bid.created_at,
|
||||
xmr_swap.contract_count,
|
||||
key_type,
|
||||
for_ed25519,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def getChainBRemoteSplitKey(swap_client, bid, xmr_swap, offer):
|
||||
|
@ -132,13 +166,21 @@ def getChainBRemoteSplitKey(swap_client, bid, xmr_swap, offer):
|
|||
|
||||
if bid.was_sent:
|
||||
if xmr_swap.a_lock_refund_spend_tx:
|
||||
af_lock_refund_spend_tx_sig = ci_leader.extractFollowerSig(xmr_swap.a_lock_refund_spend_tx)
|
||||
kbsl = ci_leader.recoverEncKey(xmr_swap.af_lock_refund_spend_tx_esig, af_lock_refund_spend_tx_sig, xmr_swap.pkasl)
|
||||
af_lock_refund_spend_tx_sig = ci_leader.extractFollowerSig(
|
||||
xmr_swap.a_lock_refund_spend_tx
|
||||
)
|
||||
kbsl = ci_leader.recoverEncKey(
|
||||
xmr_swap.af_lock_refund_spend_tx_esig,
|
||||
af_lock_refund_spend_tx_sig,
|
||||
xmr_swap.pkasl,
|
||||
)
|
||||
return ci_follower.encodeKey(kbsl)
|
||||
else:
|
||||
if xmr_swap.a_lock_spend_tx:
|
||||
al_lock_spend_tx_sig = ci_leader.extractLeaderSig(xmr_swap.a_lock_spend_tx)
|
||||
kbsf = ci_leader.recoverEncKey(xmr_swap.al_lock_spend_tx_esig, al_lock_spend_tx_sig, xmr_swap.pkasf)
|
||||
kbsf = ci_leader.recoverEncKey(
|
||||
xmr_swap.al_lock_spend_tx_esig, al_lock_spend_tx_sig, xmr_swap.pkasf
|
||||
)
|
||||
return ci_follower.encodeKey(kbsf)
|
||||
return None
|
||||
|
||||
|
@ -146,18 +188,22 @@ def getChainBRemoteSplitKey(swap_client, bid, xmr_swap, offer):
|
|||
def setDLEAG(xmr_swap, ci_to, kbsf: bytes) -> None:
|
||||
if ci_to.curve_type() == Curves.ed25519:
|
||||
xmr_swap.kbsf_dleag = ci_to.proveDLEAG(kbsf)
|
||||
xmr_swap.pkasf = xmr_swap.kbsf_dleag[0: 33]
|
||||
xmr_swap.pkasf = xmr_swap.kbsf_dleag[0:33]
|
||||
elif ci_to.curve_type() == Curves.secp256k1:
|
||||
for i in range(10):
|
||||
xmr_swap.kbsf_dleag = ci_to.signRecoverable(kbsf, 'proof kbsf owned for swap')
|
||||
pk_recovered: bytes = ci_to.verifySigAndRecover(xmr_swap.kbsf_dleag, 'proof kbsf owned for swap')
|
||||
xmr_swap.kbsf_dleag = ci_to.signRecoverable(
|
||||
kbsf, "proof kbsf owned for swap"
|
||||
)
|
||||
pk_recovered: bytes = ci_to.verifySigAndRecover(
|
||||
xmr_swap.kbsf_dleag, "proof kbsf owned for swap"
|
||||
)
|
||||
if pk_recovered == xmr_swap.pkbsf:
|
||||
break
|
||||
# self.log.debug('kbsl recovered pubkey mismatch, retrying.')
|
||||
assert (pk_recovered == xmr_swap.pkbsf)
|
||||
assert pk_recovered == xmr_swap.pkbsf
|
||||
xmr_swap.pkasf = xmr_swap.pkbsf
|
||||
else:
|
||||
raise ValueError('Unknown curve')
|
||||
raise ValueError("Unknown curve")
|
||||
|
||||
|
||||
class XmrSwapInterface(ProtocolInterface):
|
||||
|
@ -165,7 +211,7 @@ class XmrSwapInterface(ProtocolInterface):
|
|||
|
||||
def genScriptLockTxScript(self, ci, Kal: bytes, Kaf: bytes, **kwargs) -> CScript:
|
||||
# fallthrough to ci if genScriptLockTxScript is implemented there
|
||||
if hasattr(ci, 'genScriptLockTxScript') and callable(ci.genScriptLockTxScript):
|
||||
if hasattr(ci, "genScriptLockTxScript") and callable(ci.genScriptLockTxScript):
|
||||
return ci.genScriptLockTxScript(ci, Kal, Kaf, **kwargs)
|
||||
|
||||
Kal_enc = Kal if len(Kal) == 33 else ci.encodePubkey(Kal)
|
||||
|
@ -175,7 +221,9 @@ class XmrSwapInterface(ProtocolInterface):
|
|||
|
||||
def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes:
|
||||
addr_to = self.getMockAddrTo(ci)
|
||||
funded_tx = ci.createRawFundedTransaction(addr_to, amount, sub_fee, lock_unspents=False)
|
||||
funded_tx = ci.createRawFundedTransaction(
|
||||
addr_to, amount, sub_fee, lock_unspents=False
|
||||
)
|
||||
|
||||
return bytes.fromhex(funded_tx)
|
||||
|
||||
|
@ -191,9 +239,9 @@ class XmrSwapInterface(ProtocolInterface):
|
|||
found += 1
|
||||
|
||||
if found < 1:
|
||||
raise ValueError('Mocked output not found')
|
||||
raise ValueError("Mocked output not found")
|
||||
if found > 1:
|
||||
raise ValueError('Too many mocked outputs found')
|
||||
raise ValueError("Too many mocked outputs found")
|
||||
ctx.nLockTime = 0
|
||||
|
||||
return ctx.serialize()
|
||||
|
|
116
basicswap/rpc.py
116
basicswap/rpc.py
|
@ -18,31 +18,42 @@ from xmlrpc.client import (
|
|||
from .util import jsonDecimal
|
||||
|
||||
|
||||
class Jsonrpc():
|
||||
class Jsonrpc:
|
||||
# __getattr__ complicates extending ServerProxy
|
||||
def __init__(self, uri, transport=None, encoding=None, verbose=False,
|
||||
allow_none=False, use_datetime=False, use_builtin_types=False,
|
||||
*, context=None):
|
||||
def __init__(
|
||||
self,
|
||||
uri,
|
||||
transport=None,
|
||||
encoding=None,
|
||||
verbose=False,
|
||||
allow_none=False,
|
||||
use_datetime=False,
|
||||
use_builtin_types=False,
|
||||
*,
|
||||
context=None,
|
||||
):
|
||||
# establish a "logical" server connection
|
||||
|
||||
# get the url
|
||||
parsed = urllib.parse.urlparse(uri)
|
||||
if parsed.scheme not in ('http', 'https'):
|
||||
raise OSError('unsupported XML-RPC protocol')
|
||||
if parsed.scheme not in ("http", "https"):
|
||||
raise OSError("unsupported XML-RPC protocol")
|
||||
self.__host = parsed.netloc
|
||||
self.__handler = parsed.path
|
||||
if not self.__handler:
|
||||
self.__handler = '/RPC2'
|
||||
self.__handler = "/RPC2"
|
||||
|
||||
if transport is None:
|
||||
handler = SafeTransport if parsed.scheme == 'https' else Transport
|
||||
handler = SafeTransport if parsed.scheme == "https" else Transport
|
||||
extra_kwargs = {}
|
||||
transport = handler(use_datetime=use_datetime,
|
||||
use_builtin_types=use_builtin_types,
|
||||
**extra_kwargs)
|
||||
transport = handler(
|
||||
use_datetime=use_datetime,
|
||||
use_builtin_types=use_builtin_types,
|
||||
**extra_kwargs,
|
||||
)
|
||||
self.__transport = transport
|
||||
|
||||
self.__encoding = encoding or 'utf-8'
|
||||
self.__encoding = encoding or "utf-8"
|
||||
self.__verbose = verbose
|
||||
self.__allow_none = allow_none
|
||||
|
||||
|
@ -57,17 +68,16 @@ class Jsonrpc():
|
|||
connection = self.__transport.make_connection(self.__host)
|
||||
headers = self.__transport._extra_headers[:]
|
||||
|
||||
request_body = {
|
||||
'method': method,
|
||||
'params': params,
|
||||
'id': self.__request_id
|
||||
}
|
||||
request_body = {"method": method, "params": params, "id": self.__request_id}
|
||||
|
||||
connection.putrequest('POST', self.__handler)
|
||||
headers.append(('Content-Type', 'application/json'))
|
||||
headers.append(('User-Agent', 'jsonrpc'))
|
||||
connection.putrequest("POST", self.__handler)
|
||||
headers.append(("Content-Type", "application/json"))
|
||||
headers.append(("User-Agent", "jsonrpc"))
|
||||
self.__transport.send_headers(connection, headers)
|
||||
self.__transport.send_content(connection, json.dumps(request_body, default=jsonDecimal).encode('utf-8'))
|
||||
self.__transport.send_content(
|
||||
connection,
|
||||
json.dumps(request_body, default=jsonDecimal).encode("utf-8"),
|
||||
)
|
||||
self.__request_id += 1
|
||||
|
||||
resp = connection.getresponse()
|
||||
|
@ -82,55 +92,59 @@ class Jsonrpc():
|
|||
raise
|
||||
|
||||
|
||||
def callrpc(rpc_port, auth, method, params=[], wallet=None, host='127.0.0.1'):
|
||||
def callrpc(rpc_port, auth, method, params=[], wallet=None, host="127.0.0.1"):
|
||||
try:
|
||||
url = 'http://{}@{}:{}/'.format(auth, host, rpc_port)
|
||||
url = "http://{}@{}:{}/".format(auth, host, rpc_port)
|
||||
if wallet is not None:
|
||||
url += 'wallet/' + urllib.parse.quote(wallet)
|
||||
url += "wallet/" + urllib.parse.quote(wallet)
|
||||
x = Jsonrpc(url)
|
||||
|
||||
v = x.json_request(method, params)
|
||||
x.close()
|
||||
r = json.loads(v.decode('utf-8'))
|
||||
r = json.loads(v.decode("utf-8"))
|
||||
except Exception as ex:
|
||||
traceback.print_exc()
|
||||
raise ValueError('RPC server error ' + str(ex) + ', method: ' + method)
|
||||
raise ValueError("RPC server error " + str(ex) + ", method: " + method)
|
||||
|
||||
if 'error' in r and r['error'] is not None:
|
||||
raise ValueError('RPC error ' + str(r['error']))
|
||||
if "error" in r and r["error"] is not None:
|
||||
raise ValueError("RPC error " + str(r["error"]))
|
||||
|
||||
return r['result']
|
||||
return r["result"]
|
||||
|
||||
|
||||
def openrpc(rpc_port, auth, wallet=None, host='127.0.0.1'):
|
||||
def openrpc(rpc_port, auth, wallet=None, host="127.0.0.1"):
|
||||
try:
|
||||
url = 'http://{}@{}:{}/'.format(auth, host, rpc_port)
|
||||
url = "http://{}@{}:{}/".format(auth, host, rpc_port)
|
||||
if wallet is not None:
|
||||
url += 'wallet/' + urllib.parse.quote(wallet)
|
||||
url += "wallet/" + urllib.parse.quote(wallet)
|
||||
return Jsonrpc(url)
|
||||
except Exception as ex:
|
||||
traceback.print_exc()
|
||||
raise ValueError('RPC error ' + str(ex))
|
||||
raise ValueError("RPC error " + str(ex))
|
||||
|
||||
|
||||
def callrpc_cli(bindir, datadir, chain, cmd, cli_bin='particl-cli', wallet=None):
|
||||
def callrpc_cli(bindir, datadir, chain, cmd, cli_bin="particl-cli", wallet=None):
|
||||
cli_bin = os.path.join(bindir, cli_bin)
|
||||
|
||||
args = [cli_bin, ]
|
||||
if chain != 'mainnet':
|
||||
args.append('-' + chain)
|
||||
args.append('-datadir=' + datadir)
|
||||
args = [
|
||||
cli_bin,
|
||||
]
|
||||
if chain != "mainnet":
|
||||
args.append("-" + chain)
|
||||
args.append("-datadir=" + datadir)
|
||||
if wallet is not None:
|
||||
args.append('-rpcwallet=' + wallet)
|
||||
args.append("-rpcwallet=" + wallet)
|
||||
args += shlex.split(cmd)
|
||||
|
||||
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
p = subprocess.Popen(
|
||||
args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
out = p.communicate()
|
||||
|
||||
if len(out[1]) > 0:
|
||||
raise ValueError('RPC error ' + str(out[1]))
|
||||
raise ValueError("RPC error " + str(out[1]))
|
||||
|
||||
r = out[0].decode('utf-8').strip()
|
||||
r = out[0].decode("utf-8").strip()
|
||||
try:
|
||||
r = json.loads(r)
|
||||
except Exception:
|
||||
|
@ -138,7 +152,7 @@ def callrpc_cli(bindir, datadir, chain, cmd, cli_bin='particl-cli', wallet=None)
|
|||
return r
|
||||
|
||||
|
||||
def make_rpc_func(port, auth, wallet=None, host='127.0.0.1'):
|
||||
def make_rpc_func(port, auth, wallet=None, host="127.0.0.1"):
|
||||
port = port
|
||||
auth = auth
|
||||
wallet = wallet
|
||||
|
@ -146,11 +160,19 @@ def make_rpc_func(port, auth, wallet=None, host='127.0.0.1'):
|
|||
|
||||
def rpc_func(method, params=None, wallet_override=None):
|
||||
nonlocal port, auth, wallet, host
|
||||
return callrpc(port, auth, method, params, wallet if wallet_override is None else wallet_override, host)
|
||||
return callrpc(
|
||||
port,
|
||||
auth,
|
||||
method,
|
||||
params,
|
||||
wallet if wallet_override is None else wallet_override,
|
||||
host,
|
||||
)
|
||||
|
||||
return rpc_func
|
||||
|
||||
|
||||
def escape_rpcauth(auth_str: str) -> str:
|
||||
username, password = auth_str.split(':', 1)
|
||||
password = urllib.parse.quote(password, safe='')
|
||||
return f'{username}:{password}'
|
||||
username, password = auth_str.split(":", 1)
|
||||
password = urllib.parse.quote(password, safe="")
|
||||
return f"{username}:{password}"
|
||||
|
|
|
@ -33,31 +33,50 @@ class SocksTransport(Transport):
|
|||
return self._connection[1]
|
||||
# create a HTTP connection object from a host descriptor
|
||||
chost, self._extra_headers, x509 = self.get_host_info(host)
|
||||
self._connection = host, SocksiPyConnection(self.proxy_type, self.proxy_host, self.proxy_port, self.proxy_rdns, self.proxy_username, self.proxy_password, chost)
|
||||
self._connection = host, SocksiPyConnection(
|
||||
self.proxy_type,
|
||||
self.proxy_host,
|
||||
self.proxy_port,
|
||||
self.proxy_rdns,
|
||||
self.proxy_username,
|
||||
self.proxy_password,
|
||||
chost,
|
||||
)
|
||||
return self._connection[1]
|
||||
|
||||
|
||||
class JsonrpcDigest():
|
||||
class JsonrpcDigest:
|
||||
# __getattr__ complicates extending ServerProxy
|
||||
def __init__(self, uri, transport=None, encoding=None, verbose=False,
|
||||
allow_none=False, use_datetime=False, use_builtin_types=False,
|
||||
*, context=None):
|
||||
def __init__(
|
||||
self,
|
||||
uri,
|
||||
transport=None,
|
||||
encoding=None,
|
||||
verbose=False,
|
||||
allow_none=False,
|
||||
use_datetime=False,
|
||||
use_builtin_types=False,
|
||||
*,
|
||||
context=None,
|
||||
):
|
||||
|
||||
parsed = urllib.parse.urlparse(uri)
|
||||
if parsed.scheme not in ('http', 'https'):
|
||||
raise OSError('unsupported XML-RPC protocol')
|
||||
if parsed.scheme not in ("http", "https"):
|
||||
raise OSError("unsupported XML-RPC protocol")
|
||||
self.__host = parsed.netloc
|
||||
self.__handler = parsed.path
|
||||
|
||||
if transport is None:
|
||||
handler = SafeTransport if parsed.scheme == 'https' else Transport
|
||||
handler = SafeTransport if parsed.scheme == "https" else Transport
|
||||
extra_kwargs = {}
|
||||
transport = handler(use_datetime=use_datetime,
|
||||
use_builtin_types=use_builtin_types,
|
||||
**extra_kwargs)
|
||||
transport = handler(
|
||||
use_datetime=use_datetime,
|
||||
use_builtin_types=use_builtin_types,
|
||||
**extra_kwargs,
|
||||
)
|
||||
self.__transport = transport
|
||||
|
||||
self.__encoding = encoding or 'utf-8'
|
||||
self.__encoding = encoding or "utf-8"
|
||||
self.__verbose = verbose
|
||||
self.__allow_none = allow_none
|
||||
|
||||
|
@ -77,11 +96,18 @@ class JsonrpcDigest():
|
|||
connection.timeout = timeout
|
||||
headers = self.__transport._extra_headers[:]
|
||||
|
||||
connection.putrequest('POST', self.__handler)
|
||||
headers.append(('Content-Type', 'application/json'))
|
||||
headers.append(('User-Agent', 'jsonrpc'))
|
||||
connection.putrequest("POST", self.__handler)
|
||||
headers.append(("Content-Type", "application/json"))
|
||||
headers.append(("User-Agent", "jsonrpc"))
|
||||
self.__transport.send_headers(connection, headers)
|
||||
self.__transport.send_content(connection, '' if params is None else json.dumps(params, default=jsonDecimal).encode('utf-8'))
|
||||
self.__transport.send_content(
|
||||
connection,
|
||||
(
|
||||
""
|
||||
if params is None
|
||||
else json.dumps(params, default=jsonDecimal).encode("utf-8")
|
||||
),
|
||||
)
|
||||
self.__request_id += 1
|
||||
|
||||
resp = connection.getresponse()
|
||||
|
@ -93,7 +119,7 @@ class JsonrpcDigest():
|
|||
self.__transport.close()
|
||||
raise
|
||||
|
||||
def json_request(self, request_body, username='', password='', timeout=None):
|
||||
def json_request(self, request_body, username="", password="", timeout=None):
|
||||
try:
|
||||
connection = self.__transport.make_connection(self.__host)
|
||||
if timeout:
|
||||
|
@ -101,65 +127,82 @@ class JsonrpcDigest():
|
|||
|
||||
headers = self.__transport._extra_headers[:]
|
||||
|
||||
connection.putrequest('POST', self.__handler)
|
||||
headers.append(('Content-Type', 'application/json'))
|
||||
headers.append(('Connection', 'keep-alive'))
|
||||
connection.putrequest("POST", self.__handler)
|
||||
headers.append(("Content-Type", "application/json"))
|
||||
headers.append(("Connection", "keep-alive"))
|
||||
self.__transport.send_headers(connection, headers)
|
||||
self.__transport.send_content(connection, json.dumps(request_body, default=jsonDecimal).encode('utf-8') if request_body else '')
|
||||
self.__transport.send_content(
|
||||
connection,
|
||||
(
|
||||
json.dumps(request_body, default=jsonDecimal).encode("utf-8")
|
||||
if request_body
|
||||
else ""
|
||||
),
|
||||
)
|
||||
resp = connection.getresponse()
|
||||
|
||||
if resp.status == 401:
|
||||
resp_headers = resp.getheaders()
|
||||
v = resp.read()
|
||||
_ = resp.read()
|
||||
|
||||
algorithm = ''
|
||||
realm = ''
|
||||
nonce = ''
|
||||
realm = ""
|
||||
nonce = ""
|
||||
for h in resp_headers:
|
||||
if h[0] != 'WWW-authenticate':
|
||||
if h[0] != "WWW-authenticate":
|
||||
continue
|
||||
fields = h[1].split(',')
|
||||
fields = h[1].split(",")
|
||||
for f in fields:
|
||||
key, value = f.split('=', 1)
|
||||
if key == 'algorithm' and value != 'MD5':
|
||||
key, value = f.split("=", 1)
|
||||
if key == "algorithm" and value != "MD5":
|
||||
break
|
||||
if key == 'realm':
|
||||
if key == "realm":
|
||||
realm = value.strip('"')
|
||||
if key == 'nonce':
|
||||
if key == "nonce":
|
||||
nonce = value.strip('"')
|
||||
if realm != '' and nonce != '':
|
||||
if realm != "" and nonce != "":
|
||||
break
|
||||
|
||||
if realm == '' or nonce == '':
|
||||
raise ValueError('Authenticate header not found.')
|
||||
if realm == "" or nonce == "":
|
||||
raise ValueError("Authenticate header not found.")
|
||||
|
||||
path = self.__handler
|
||||
HA1 = hashlib.md5(f'{username}:{realm}:{password}'.encode('utf-8')).hexdigest()
|
||||
HA1 = hashlib.md5(
|
||||
f"{username}:{realm}:{password}".encode("utf-8")
|
||||
).hexdigest()
|
||||
|
||||
http_method = 'POST'
|
||||
HA2 = hashlib.md5(f'{http_method}:{path}'.encode('utf-8')).hexdigest()
|
||||
http_method = "POST"
|
||||
HA2 = hashlib.md5(f"{http_method}:{path}".encode("utf-8")).hexdigest()
|
||||
|
||||
ncvalue = '{:08x}'.format(1)
|
||||
s = ncvalue.encode('utf-8')
|
||||
s += nonce.encode('utf-8')
|
||||
s += time.ctime().encode('utf-8')
|
||||
ncvalue = "{:08x}".format(1)
|
||||
s = ncvalue.encode("utf-8")
|
||||
s += nonce.encode("utf-8")
|
||||
s += time.ctime().encode("utf-8")
|
||||
s += os.urandom(8)
|
||||
cnonce = (hashlib.sha1(s).hexdigest()[:16])
|
||||
cnonce = hashlib.sha1(s).hexdigest()[:16]
|
||||
|
||||
# MD5-SESS
|
||||
HA1 = hashlib.md5(f'{HA1}:{nonce}:{cnonce}'.encode('utf-8')).hexdigest()
|
||||
HA1 = hashlib.md5(f"{HA1}:{nonce}:{cnonce}".encode("utf-8")).hexdigest()
|
||||
|
||||
respdig = hashlib.md5(f'{HA1}:{nonce}:{ncvalue}:{cnonce}:auth:{HA2}'.encode('utf-8')).hexdigest()
|
||||
respdig = hashlib.md5(
|
||||
f"{HA1}:{nonce}:{ncvalue}:{cnonce}:auth:{HA2}".encode("utf-8")
|
||||
).hexdigest()
|
||||
|
||||
header_value = f'Digest username="{username}", realm="{realm}", nonce="{nonce}", uri="{path}", response="{respdig}", algorithm="MD5-sess", qop="auth", nc={ncvalue}, cnonce="{cnonce}"'
|
||||
headers = self.__transport._extra_headers[:]
|
||||
headers.append(('Authorization', header_value))
|
||||
headers.append(("Authorization", header_value))
|
||||
|
||||
connection.putrequest('POST', self.__handler)
|
||||
headers.append(('Content-Type', 'application/json'))
|
||||
headers.append(('Connection', 'keep-alive'))
|
||||
connection.putrequest("POST", self.__handler)
|
||||
headers.append(("Content-Type", "application/json"))
|
||||
headers.append(("Connection", "keep-alive"))
|
||||
self.__transport.send_headers(connection, headers)
|
||||
self.__transport.send_content(connection, json.dumps(request_body, default=jsonDecimal).encode('utf-8') if request_body else '')
|
||||
self.__transport.send_content(
|
||||
connection,
|
||||
(
|
||||
json.dumps(request_body, default=jsonDecimal).encode("utf-8")
|
||||
if request_body
|
||||
else ""
|
||||
),
|
||||
)
|
||||
resp = connection.getresponse()
|
||||
|
||||
self.__request_id += 1
|
||||
|
@ -172,57 +215,88 @@ class JsonrpcDigest():
|
|||
raise
|
||||
|
||||
|
||||
def callrpc_xmr(rpc_port, method, params=[], rpc_host='127.0.0.1', path='json_rpc', auth=None, timeout=120, transport=None, tag=''):
|
||||
def callrpc_xmr(
|
||||
rpc_port,
|
||||
method,
|
||||
params=[],
|
||||
rpc_host="127.0.0.1",
|
||||
path="json_rpc",
|
||||
auth=None,
|
||||
timeout=120,
|
||||
transport=None,
|
||||
tag="",
|
||||
):
|
||||
# auth is a tuple: (username, password)
|
||||
try:
|
||||
if rpc_host.count('://') > 0:
|
||||
url = '{}:{}/{}'.format(rpc_host, rpc_port, path)
|
||||
if rpc_host.count("://") > 0:
|
||||
url = "{}:{}/{}".format(rpc_host, rpc_port, path)
|
||||
else:
|
||||
url = 'http://{}:{}/{}'.format(rpc_host, rpc_port, path)
|
||||
url = "http://{}:{}/{}".format(rpc_host, rpc_port, path)
|
||||
|
||||
x = JsonrpcDigest(url, transport=transport)
|
||||
request_body = {
|
||||
'method': method,
|
||||
'params': params,
|
||||
'jsonrpc': '2.0',
|
||||
'id': x.request_id()
|
||||
"method": method,
|
||||
"params": params,
|
||||
"jsonrpc": "2.0",
|
||||
"id": x.request_id(),
|
||||
}
|
||||
if auth:
|
||||
v = x.json_request(request_body, username=auth[0], password=auth[1], timeout=timeout)
|
||||
v = x.json_request(
|
||||
request_body, username=auth[0], password=auth[1], timeout=timeout
|
||||
)
|
||||
else:
|
||||
v = x.json_request(request_body, timeout=timeout)
|
||||
x.close()
|
||||
r = json.loads(v.decode('utf-8'))
|
||||
r = json.loads(v.decode("utf-8"))
|
||||
except Exception as ex:
|
||||
raise ValueError('{}RPC Server Error: {}'.format(tag, str(ex)))
|
||||
raise ValueError("{}RPC Server Error: {}".format(tag, str(ex)))
|
||||
|
||||
if 'error' in r and r['error'] is not None:
|
||||
raise ValueError(tag + 'RPC error ' + str(r['error']))
|
||||
if "error" in r and r["error"] is not None:
|
||||
raise ValueError(tag + "RPC error " + str(r["error"]))
|
||||
|
||||
return r['result']
|
||||
return r["result"]
|
||||
|
||||
|
||||
def callrpc_xmr2(rpc_port: int, method: str, params=None, auth=None, rpc_host='127.0.0.1', timeout=120, transport=None, tag=''):
|
||||
def callrpc_xmr2(
|
||||
rpc_port: int,
|
||||
method: str,
|
||||
params=None,
|
||||
auth=None,
|
||||
rpc_host="127.0.0.1",
|
||||
timeout=120,
|
||||
transport=None,
|
||||
tag="",
|
||||
):
|
||||
try:
|
||||
if rpc_host.count('://') > 0:
|
||||
url = '{}:{}/{}'.format(rpc_host, rpc_port, method)
|
||||
if rpc_host.count("://") > 0:
|
||||
url = "{}:{}/{}".format(rpc_host, rpc_port, method)
|
||||
else:
|
||||
url = 'http://{}:{}/{}'.format(rpc_host, rpc_port, method)
|
||||
url = "http://{}:{}/{}".format(rpc_host, rpc_port, method)
|
||||
|
||||
x = JsonrpcDigest(url, transport=transport)
|
||||
if auth:
|
||||
v = x.json_request(params, username=auth[0], password=auth[1], timeout=timeout)
|
||||
v = x.json_request(
|
||||
params, username=auth[0], password=auth[1], timeout=timeout
|
||||
)
|
||||
else:
|
||||
v = x.json_request(params, timeout=timeout)
|
||||
x.close()
|
||||
r = json.loads(v.decode('utf-8'))
|
||||
r = json.loads(v.decode("utf-8"))
|
||||
except Exception as ex:
|
||||
raise ValueError('{}RPC Server Error: {}'.format(tag, str(ex)))
|
||||
raise ValueError("{}RPC Server Error: {}".format(tag, str(ex)))
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def make_xmr_rpc2_func(port, auth, host='127.0.0.1', proxy_host=None, proxy_port=None, default_timeout=120, tag=''):
|
||||
def make_xmr_rpc2_func(
|
||||
port,
|
||||
auth,
|
||||
host="127.0.0.1",
|
||||
proxy_host=None,
|
||||
proxy_port=None,
|
||||
default_timeout=120,
|
||||
tag="",
|
||||
):
|
||||
port = port
|
||||
auth = auth
|
||||
host = host
|
||||
|
@ -236,11 +310,29 @@ def make_xmr_rpc2_func(port, auth, host='127.0.0.1', proxy_host=None, proxy_port
|
|||
|
||||
def rpc_func(method, params=None, wallet=None, timeout=default_timeout):
|
||||
nonlocal port, auth, host, transport, tag
|
||||
return callrpc_xmr2(port, method, params, auth=auth, rpc_host=host, timeout=timeout, transport=transport, tag=tag)
|
||||
return callrpc_xmr2(
|
||||
port,
|
||||
method,
|
||||
params,
|
||||
auth=auth,
|
||||
rpc_host=host,
|
||||
timeout=timeout,
|
||||
transport=transport,
|
||||
tag=tag,
|
||||
)
|
||||
|
||||
return rpc_func
|
||||
|
||||
|
||||
def make_xmr_rpc_func(port, auth, host='127.0.0.1', proxy_host=None, proxy_port=None, default_timeout=120, tag=''):
|
||||
def make_xmr_rpc_func(
|
||||
port,
|
||||
auth,
|
||||
host="127.0.0.1",
|
||||
proxy_host=None,
|
||||
proxy_port=None,
|
||||
default_timeout=120,
|
||||
tag="",
|
||||
):
|
||||
port = port
|
||||
auth = auth
|
||||
host = host
|
||||
|
@ -254,5 +346,15 @@ def make_xmr_rpc_func(port, auth, host='127.0.0.1', proxy_host=None, proxy_port=
|
|||
|
||||
def rpc_func(method, params=None, wallet=None, timeout=default_timeout):
|
||||
nonlocal port, auth, host, transport, tag
|
||||
return callrpc_xmr(port, method, params, rpc_host=host, auth=auth, timeout=timeout, transport=transport, tag=tag)
|
||||
return callrpc_xmr(
|
||||
port,
|
||||
method,
|
||||
params,
|
||||
rpc_host=host,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
transport=transport,
|
||||
tag=tag,
|
||||
)
|
||||
|
||||
return rpc_func
|
||||
|
|
|
@ -8,23 +8,23 @@ from enum import IntEnum
|
|||
|
||||
|
||||
class OpCodes(IntEnum):
|
||||
OP_0 = 0x00,
|
||||
OP_PUSHDATA1 = 0x4c,
|
||||
OP_1 = 0x51,
|
||||
OP_16 = 0x60,
|
||||
OP_IF = 0x63,
|
||||
OP_ELSE = 0x67,
|
||||
OP_ENDIF = 0x68,
|
||||
OP_RETURN = 0x6a,
|
||||
OP_DROP = 0x75,
|
||||
OP_DUP = 0x76,
|
||||
OP_SIZE = 0x82,
|
||||
OP_EQUAL = 0x87,
|
||||
OP_EQUALVERIFY = 0x88,
|
||||
OP_SHA256 = 0xa8,
|
||||
OP_HASH160 = 0xa9,
|
||||
OP_CHECKSIG = 0xac,
|
||||
OP_CHECKLOCKTIMEVERIFY = 0xb1,
|
||||
OP_CHECKSEQUENCEVERIFY = 0xb2,
|
||||
OP_0 = (0x00,)
|
||||
OP_PUSHDATA1 = (0x4C,)
|
||||
OP_1 = (0x51,)
|
||||
OP_16 = (0x60,)
|
||||
OP_IF = (0x63,)
|
||||
OP_ELSE = (0x67,)
|
||||
OP_ENDIF = (0x68,)
|
||||
OP_RETURN = (0x6A,)
|
||||
OP_DROP = (0x75,)
|
||||
OP_DUP = (0x76,)
|
||||
OP_SIZE = (0x82,)
|
||||
OP_EQUAL = (0x87,)
|
||||
OP_EQUALVERIFY = (0x88,)
|
||||
OP_SHA256 = (0xA8,)
|
||||
OP_HASH160 = (0xA9,)
|
||||
OP_CHECKSIG = (0xAC,)
|
||||
OP_CHECKLOCKTIMEVERIFY = (0xB1,)
|
||||
OP_CHECKSEQUENCEVERIFY = (0xB2,)
|
||||
|
||||
OP_SHA256_DECRED = 0xc0,
|
||||
OP_SHA256_DECRED = (0xC0,)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022-2023 tecnovert
|
||||
# Copyright (c) 2024 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -28,33 +29,33 @@ def page_automation_strategies(self, url_split, post_string):
|
|||
summary = swap_client.getSummary()
|
||||
|
||||
filters = {
|
||||
'page_no': 1,
|
||||
'limit': PAGE_LIMIT,
|
||||
'sort_by': 'created_at',
|
||||
'sort_dir': 'desc',
|
||||
"page_no": 1,
|
||||
"limit": PAGE_LIMIT,
|
||||
"sort_by": "created_at",
|
||||
"sort_dir": "desc",
|
||||
}
|
||||
|
||||
messages = []
|
||||
form_data = self.checkForm(post_string, 'automationstrategies', messages)
|
||||
form_data = self.checkForm(post_string, "automationstrategies", messages)
|
||||
|
||||
if form_data:
|
||||
if have_data_entry(form_data, 'clearfilters'):
|
||||
swap_client.clearFilters('page_automation_strategies')
|
||||
if have_data_entry(form_data, "clearfilters"):
|
||||
swap_client.clearFilters("page_automation_strategies")
|
||||
else:
|
||||
if have_data_entry(form_data, 'sort_by'):
|
||||
sort_by = get_data_entry(form_data, 'sort_by')
|
||||
ensure(sort_by in ['created_at', 'rate'], 'Invalid sort by')
|
||||
filters['sort_by'] = sort_by
|
||||
if have_data_entry(form_data, 'sort_dir'):
|
||||
sort_dir = get_data_entry(form_data, 'sort_dir')
|
||||
ensure(sort_dir in ['asc', 'desc'], 'Invalid sort dir')
|
||||
filters['sort_dir'] = sort_dir
|
||||
if have_data_entry(form_data, "sort_by"):
|
||||
sort_by = get_data_entry(form_data, "sort_by")
|
||||
ensure(sort_by in ["created_at", "rate"], "Invalid sort by")
|
||||
filters["sort_by"] = sort_by
|
||||
if have_data_entry(form_data, "sort_dir"):
|
||||
sort_dir = get_data_entry(form_data, "sort_dir")
|
||||
ensure(sort_dir in ["asc", "desc"], "Invalid sort dir")
|
||||
filters["sort_dir"] = sort_dir
|
||||
|
||||
set_pagination_filters(form_data, filters)
|
||||
if have_data_entry(form_data, 'applyfilters'):
|
||||
swap_client.setFilters('page_automation_strategies', filters)
|
||||
if have_data_entry(form_data, "applyfilters"):
|
||||
swap_client.setFilters("page_automation_strategies", filters)
|
||||
else:
|
||||
saved_filters = swap_client.getFilters('page_automation_strategies')
|
||||
saved_filters = swap_client.getFilters("page_automation_strategies")
|
||||
if saved_filters:
|
||||
filters.update(saved_filters)
|
||||
|
||||
|
@ -62,13 +63,16 @@ def page_automation_strategies(self, url_split, post_string):
|
|||
for s in swap_client.listAutomationStrategies(filters):
|
||||
formatted_strategies.append((s[0], s[1], strConcepts(s[2])))
|
||||
|
||||
template = server.env.get_template('automation_strategies.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'filters': filters,
|
||||
'strategies': formatted_strategies,
|
||||
'summary': summary,
|
||||
})
|
||||
template = server.env.get_template("automation_strategies.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"messages": messages,
|
||||
"filters": filters,
|
||||
"strategies": formatted_strategies,
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def page_automation_strategy_new(self, url_split, post_string):
|
||||
|
@ -78,21 +82,24 @@ def page_automation_strategy_new(self, url_split, post_string):
|
|||
summary = swap_client.getSummary()
|
||||
|
||||
messages = []
|
||||
form_data = self.checkForm(post_string, 'automationstrategynew', messages)
|
||||
_ = self.checkForm(post_string, "automationstrategynew", messages)
|
||||
|
||||
template = server.env.get_template('automation_strategy_new.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'summary': summary,
|
||||
})
|
||||
template = server.env.get_template("automation_strategy_new.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"messages": messages,
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def page_automation_strategy(self, url_split, post_string):
|
||||
ensure(len(url_split) > 2, 'Strategy ID not specified')
|
||||
ensure(len(url_split) > 2, "Strategy ID not specified")
|
||||
try:
|
||||
strategy_id = int(url_split[2])
|
||||
except Exception:
|
||||
raise ValueError('Bad strategy ID')
|
||||
raise ValueError("Bad strategy ID")
|
||||
|
||||
server = self.server
|
||||
swap_client = server.swap_client
|
||||
|
@ -101,17 +108,17 @@ def page_automation_strategy(self, url_split, post_string):
|
|||
|
||||
messages = []
|
||||
err_messages = []
|
||||
form_data = self.checkForm(post_string, 'automation_strategy', err_messages)
|
||||
form_data = self.checkForm(post_string, "automation_strategy", err_messages)
|
||||
show_edit_form = False
|
||||
if form_data:
|
||||
if have_data_entry(form_data, 'edit'):
|
||||
if have_data_entry(form_data, "edit"):
|
||||
show_edit_form = True
|
||||
if have_data_entry(form_data, 'apply'):
|
||||
if have_data_entry(form_data, "apply"):
|
||||
try:
|
||||
data = json.loads(get_data_entry_or(form_data, 'data', ''))
|
||||
note = get_data_entry_or(form_data, 'note', '')
|
||||
data = json.loads(get_data_entry_or(form_data, "data", ""))
|
||||
note = get_data_entry_or(form_data, "note", "")
|
||||
swap_client.updateAutomationStrategy(strategy_id, data, note)
|
||||
messages.append('Updated')
|
||||
messages.append("Updated")
|
||||
except Exception as e:
|
||||
err_messages.append(str(e))
|
||||
show_edit_form = True
|
||||
|
@ -119,19 +126,24 @@ def page_automation_strategy(self, url_split, post_string):
|
|||
strategy = swap_client.getAutomationStrategy(strategy_id)
|
||||
|
||||
formatted_strategy = {
|
||||
'label': strategy.label,
|
||||
'type': strConcepts(strategy.type_ind),
|
||||
'only_known_identities': 'True' if strategy.only_known_identities is True else 'False',
|
||||
'data': strategy.data.decode('utf-8'),
|
||||
'note': '' if not strategy.note else strategy.note,
|
||||
'created_at': strategy.created_at,
|
||||
"label": strategy.label,
|
||||
"type": strConcepts(strategy.type_ind),
|
||||
"only_known_identities": (
|
||||
"True" if strategy.only_known_identities is True else "False"
|
||||
),
|
||||
"data": strategy.data.decode("utf-8"),
|
||||
"note": "" if not strategy.note else strategy.note,
|
||||
"created_at": strategy.created_at,
|
||||
}
|
||||
|
||||
template = server.env.get_template('automation_strategy.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'strategy': formatted_strategy,
|
||||
'show_edit_form': show_edit_form,
|
||||
'summary': summary,
|
||||
})
|
||||
template = server.env.get_template("automation_strategy.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"messages": messages,
|
||||
"err_messages": err_messages,
|
||||
"strategy": formatted_strategy,
|
||||
"show_edit_form": show_edit_form,
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022-2024 tecnovert
|
||||
# Copyright (c) 2024 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -30,12 +31,12 @@ from basicswap.basicswap_util import (
|
|||
|
||||
|
||||
def page_bid(self, url_split, post_string):
|
||||
ensure(len(url_split) > 2, 'Bid ID not specified')
|
||||
ensure(len(url_split) > 2, "Bid ID not specified")
|
||||
try:
|
||||
bid_id = bytes.fromhex(url_split[2])
|
||||
assert len(bid_id) == 28
|
||||
except Exception:
|
||||
raise ValueError('Bad bid ID')
|
||||
raise ValueError("Bad bid ID")
|
||||
server = self.server
|
||||
swap_client = server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
|
@ -49,129 +50,163 @@ def page_bid(self, url_split, post_string):
|
|||
show_lock_transfers = False
|
||||
edit_bid = False
|
||||
view_tx_ind = None
|
||||
form_data = self.checkForm(post_string, 'bid', err_messages)
|
||||
form_data = self.checkForm(post_string, "bid", err_messages)
|
||||
if form_data:
|
||||
if b'abandon_bid' in form_data:
|
||||
if b"abandon_bid" in form_data:
|
||||
try:
|
||||
swap_client.abandonBid(bid_id)
|
||||
messages.append('Bid abandoned')
|
||||
messages.append("Bid abandoned")
|
||||
except Exception as ex:
|
||||
err_messages.append('Abandon failed ' + str(ex))
|
||||
elif b'accept_bid' in form_data:
|
||||
err_messages.append("Abandon failed " + str(ex))
|
||||
elif b"accept_bid" in form_data:
|
||||
try:
|
||||
swap_client.acceptBid(bid_id)
|
||||
messages.append('Bid accepted')
|
||||
messages.append("Bid accepted")
|
||||
except Exception as ex:
|
||||
err_messages.append('Accept failed ' + str(ex))
|
||||
elif b'show_txns' in form_data:
|
||||
err_messages.append("Accept failed " + str(ex))
|
||||
elif b"show_txns" in form_data:
|
||||
show_txns = True
|
||||
elif b'show_offerer_seq_diagram' in form_data:
|
||||
elif b"show_offerer_seq_diagram" in form_data:
|
||||
show_offerer_seq_diagram = True
|
||||
elif b'show_bidder_seq_diagram' in form_data:
|
||||
elif b"show_bidder_seq_diagram" in form_data:
|
||||
show_bidder_seq_diagram = True
|
||||
elif b'edit_bid' in form_data:
|
||||
elif b"edit_bid" in form_data:
|
||||
edit_bid = True
|
||||
elif b'edit_bid_submit' in form_data:
|
||||
elif b"edit_bid_submit" in form_data:
|
||||
data = {
|
||||
'bid_state': int(form_data[b'new_state'][0]),
|
||||
'bid_action': int(get_data_entry_or(form_data, 'new_action', -1)),
|
||||
'debug_ind': int(get_data_entry_or(form_data, 'debugind', -1)),
|
||||
'kbs_other': get_data_entry_or(form_data, 'kbs_other', None),
|
||||
"bid_state": int(form_data[b"new_state"][0]),
|
||||
"bid_action": int(get_data_entry_or(form_data, "new_action", -1)),
|
||||
"debug_ind": int(get_data_entry_or(form_data, "debugind", -1)),
|
||||
"kbs_other": get_data_entry_or(form_data, "kbs_other", None),
|
||||
}
|
||||
try:
|
||||
swap_client.manualBidUpdate(bid_id, data)
|
||||
messages.append('Bid edited')
|
||||
messages.append("Bid edited")
|
||||
except Exception as ex:
|
||||
err_messages.append('Edit failed ' + str(ex))
|
||||
elif b'view_tx_submit' in form_data:
|
||||
err_messages.append("Edit failed " + str(ex))
|
||||
elif b"view_tx_submit" in form_data:
|
||||
show_txns = True
|
||||
view_tx_ind = form_data[b'view_tx'][0].decode('utf-8')
|
||||
view_tx_ind = form_data[b"view_tx"][0].decode("utf-8")
|
||||
if len(view_tx_ind) != 64:
|
||||
err_messages.append('Invalid transaction selected.')
|
||||
err_messages.append("Invalid transaction selected.")
|
||||
view_tx_ind = None
|
||||
elif b'view_lock_transfers' in form_data:
|
||||
elif b"view_lock_transfers" in form_data:
|
||||
show_txns = True
|
||||
show_lock_transfers = True
|
||||
|
||||
bid, xmr_swap, offer, xmr_offer, events = swap_client.getXmrBidAndOffer(bid_id)
|
||||
ensure(bid, 'Unknown bid ID')
|
||||
ensure(bid, "Unknown bid ID")
|
||||
|
||||
data = describeBid(swap_client, bid, xmr_swap, offer, xmr_offer, events, edit_bid, show_txns, view_tx_ind, show_lock_transfers=show_lock_transfers)
|
||||
data = describeBid(
|
||||
swap_client,
|
||||
bid,
|
||||
xmr_swap,
|
||||
offer,
|
||||
xmr_offer,
|
||||
events,
|
||||
edit_bid,
|
||||
show_txns,
|
||||
view_tx_ind,
|
||||
show_lock_transfers=show_lock_transfers,
|
||||
)
|
||||
|
||||
if bid.debug_ind is not None and bid.debug_ind > 0:
|
||||
messages.append('Debug flag set: {}, {}'.format(bid.debug_ind, DebugTypes(bid.debug_ind).name))
|
||||
messages.append(
|
||||
"Debug flag set: {}, {}".format(
|
||||
bid.debug_ind, DebugTypes(bid.debug_ind).name
|
||||
)
|
||||
)
|
||||
|
||||
data['show_bidder_seq_diagram'] = show_bidder_seq_diagram
|
||||
data['show_offerer_seq_diagram'] = show_offerer_seq_diagram
|
||||
data["show_bidder_seq_diagram"] = show_bidder_seq_diagram
|
||||
data["show_offerer_seq_diagram"] = show_offerer_seq_diagram
|
||||
|
||||
old_states = listOldBidStates(bid)
|
||||
|
||||
if len(data['addr_from_label']) > 0:
|
||||
data['addr_from_label'] = '(' + data['addr_from_label'] + ')'
|
||||
data['can_accept_bid'] = True if bid.state == BidStates.BID_RECEIVED else False
|
||||
if len(data["addr_from_label"]) > 0:
|
||||
data["addr_from_label"] = "(" + data["addr_from_label"] + ")"
|
||||
data["can_accept_bid"] = True if bid.state == BidStates.BID_RECEIVED else False
|
||||
|
||||
if swap_client.debug_ui:
|
||||
data['bid_actions'] = [(-1, 'None'), ] + listBidActions()
|
||||
data["bid_actions"] = [
|
||||
(-1, "None"),
|
||||
] + listBidActions()
|
||||
|
||||
template = server.env.get_template('bid_xmr.html' if offer.swap_type == SwapTypes.XMR_SWAP else 'bid.html')
|
||||
return self.render_template(template, {
|
||||
'bid_id': bid_id.hex(),
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'data': data,
|
||||
'edit_bid': edit_bid,
|
||||
'old_states': old_states,
|
||||
'summary': summary,
|
||||
})
|
||||
template = server.env.get_template(
|
||||
"bid_xmr.html" if offer.swap_type == SwapTypes.XMR_SWAP else "bid.html"
|
||||
)
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"bid_id": bid_id.hex(),
|
||||
"messages": messages,
|
||||
"err_messages": err_messages,
|
||||
"data": data,
|
||||
"edit_bid": edit_bid,
|
||||
"old_states": old_states,
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def page_bids(self, url_split, post_string, sent=False, available=False, received=False):
|
||||
def page_bids(
|
||||
self, url_split, post_string, sent=False, available=False, received=False
|
||||
):
|
||||
server = self.server
|
||||
swap_client = server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
summary = swap_client.getSummary()
|
||||
|
||||
filters = {
|
||||
'page_no': 1,
|
||||
'bid_state_ind': -1,
|
||||
'with_expired': True,
|
||||
'limit': PAGE_LIMIT,
|
||||
'sort_by': 'created_at',
|
||||
'sort_dir': 'desc',
|
||||
"page_no": 1,
|
||||
"bid_state_ind": -1,
|
||||
"with_expired": True,
|
||||
"limit": PAGE_LIMIT,
|
||||
"sort_by": "created_at",
|
||||
"sort_dir": "desc",
|
||||
}
|
||||
if available:
|
||||
filters['bid_state_ind'] = BidStates.BID_RECEIVED
|
||||
filters['with_expired'] = False
|
||||
filters["bid_state_ind"] = BidStates.BID_RECEIVED
|
||||
filters["with_expired"] = False
|
||||
|
||||
filter_prefix = 'page_bids_sent' if sent else 'page_bids_available' if available else 'page_bids_received'
|
||||
filter_prefix = (
|
||||
"page_bids_sent"
|
||||
if sent
|
||||
else "page_bids_available" if available else "page_bids_received"
|
||||
)
|
||||
messages = []
|
||||
form_data = self.checkForm(post_string, 'bids', messages)
|
||||
form_data = self.checkForm(post_string, "bids", messages)
|
||||
if form_data:
|
||||
if have_data_entry(form_data, 'clearfilters'):
|
||||
if have_data_entry(form_data, "clearfilters"):
|
||||
swap_client.clearFilters(filter_prefix)
|
||||
else:
|
||||
if have_data_entry(form_data, 'sort_by'):
|
||||
sort_by = get_data_entry(form_data, 'sort_by')
|
||||
ensure(sort_by in ['created_at', ], 'Invalid sort by')
|
||||
filters['sort_by'] = sort_by
|
||||
if have_data_entry(form_data, 'sort_dir'):
|
||||
sort_dir = get_data_entry(form_data, 'sort_dir')
|
||||
ensure(sort_dir in ['asc', 'desc'], 'Invalid sort dir')
|
||||
filters['sort_dir'] = sort_dir
|
||||
if have_data_entry(form_data, 'state'):
|
||||
state_ind = int(get_data_entry(form_data, 'state'))
|
||||
if have_data_entry(form_data, "sort_by"):
|
||||
sort_by = get_data_entry(form_data, "sort_by")
|
||||
ensure(
|
||||
sort_by
|
||||
in [
|
||||
"created_at",
|
||||
],
|
||||
"Invalid sort by",
|
||||
)
|
||||
filters["sort_by"] = sort_by
|
||||
if have_data_entry(form_data, "sort_dir"):
|
||||
sort_dir = get_data_entry(form_data, "sort_dir")
|
||||
ensure(sort_dir in ["asc", "desc"], "Invalid sort dir")
|
||||
filters["sort_dir"] = sort_dir
|
||||
if have_data_entry(form_data, "state"):
|
||||
state_ind = int(get_data_entry(form_data, "state"))
|
||||
if state_ind != -1:
|
||||
try:
|
||||
state = BidStates(state_ind)
|
||||
except Exception:
|
||||
raise ValueError('Invalid state')
|
||||
filters['bid_state_ind'] = state_ind
|
||||
if have_data_entry(form_data, 'with_expired'):
|
||||
with_expired = toBool(get_data_entry(form_data, 'with_expired'))
|
||||
filters['with_expired'] = with_expired
|
||||
_ = BidStates(state_ind)
|
||||
except Exception as e: # noqa: F841
|
||||
raise ValueError("Invalid state")
|
||||
filters["bid_state_ind"] = state_ind
|
||||
if have_data_entry(form_data, "with_expired"):
|
||||
with_expired = toBool(get_data_entry(form_data, "with_expired"))
|
||||
filters["with_expired"] = with_expired
|
||||
|
||||
set_pagination_filters(form_data, filters)
|
||||
if have_data_entry(form_data, 'applyfilters'):
|
||||
if have_data_entry(form_data, "applyfilters"):
|
||||
swap_client.setFilters(filter_prefix, filters)
|
||||
else:
|
||||
saved_filters = swap_client.getFilters(filter_prefix)
|
||||
|
@ -181,21 +216,40 @@ def page_bids(self, url_split, post_string, sent=False, available=False, receive
|
|||
bids = swap_client.listBids(sent=sent, filters=filters)
|
||||
|
||||
page_data = {
|
||||
'bid_states': listBidStates(),
|
||||
"bid_states": listBidStates(),
|
||||
}
|
||||
template = server.env.get_template('bids.html')
|
||||
return self.render_template(template, {
|
||||
'page_type_sent': 'Bids Sent' if sent else '',
|
||||
'page_type_available': 'Bids Available' if available else '',
|
||||
'page_type_received': 'Received Bids' if received else '',
|
||||
'page_type_sent_description': 'All the bids you have placed on offers.' if sent else '',
|
||||
'page_type_available_description': 'Bids available for you to accept.' if available else '',
|
||||
'page_type_received_description': 'All the bids placed on your offers.' if received else '',
|
||||
'messages': messages,
|
||||
'filters': filters,
|
||||
'data': page_data,
|
||||
'summary': summary,
|
||||
'bids': [(format_timestamp(b[0]),
|
||||
b[2].hex(), b[3].hex(), strBidState(b[5]), strTxState(b[7]), strTxState(b[8]), b[11]) for b in bids],
|
||||
'bids_count': len(bids),
|
||||
})
|
||||
template = server.env.get_template("bids.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"page_type_sent": "Bids Sent" if sent else "",
|
||||
"page_type_available": "Bids Available" if available else "",
|
||||
"page_type_received": "Received Bids" if received else "",
|
||||
"page_type_sent_description": (
|
||||
"All the bids you have placed on offers." if sent else ""
|
||||
),
|
||||
"page_type_available_description": (
|
||||
"Bids available for you to accept." if available else ""
|
||||
),
|
||||
"page_type_received_description": (
|
||||
"All the bids placed on your offers." if received else ""
|
||||
),
|
||||
"messages": messages,
|
||||
"filters": filters,
|
||||
"data": page_data,
|
||||
"summary": summary,
|
||||
"bids": [
|
||||
(
|
||||
format_timestamp(b[0]),
|
||||
b[2].hex(),
|
||||
b[3].hex(),
|
||||
strBidState(b[5]),
|
||||
strTxState(b[7]),
|
||||
strTxState(b[8]),
|
||||
b[11],
|
||||
)
|
||||
for b in bids
|
||||
],
|
||||
"bids_count": len(bids),
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2023 The BSX Developers
|
||||
# Copyright (c) 2023-2024 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -25,31 +25,34 @@ def page_debug(self, url_split, post_string):
|
|||
result = None
|
||||
messages = []
|
||||
err_messages = []
|
||||
form_data = self.checkForm(post_string, 'wallets', err_messages)
|
||||
form_data = self.checkForm(post_string, "wallets", err_messages)
|
||||
if form_data:
|
||||
if have_data_entry(form_data, 'reinit_xmr'):
|
||||
if have_data_entry(form_data, "reinit_xmr"):
|
||||
try:
|
||||
swap_client.initialiseWallet(Coins.XMR)
|
||||
messages.append('Done.')
|
||||
messages.append("Done.")
|
||||
except Exception as e:
|
||||
err_messages.append('Failed.')
|
||||
err_messages.append(f"Failed: {e}.")
|
||||
|
||||
if have_data_entry(form_data, 'remove_expired'):
|
||||
if have_data_entry(form_data, "remove_expired"):
|
||||
try:
|
||||
swap_client.log.warning('Removing expired data.')
|
||||
swap_client.log.warning("Removing expired data.")
|
||||
remove_expired_data(swap_client)
|
||||
messages.append('Done.')
|
||||
messages.append("Done.")
|
||||
except Exception as e:
|
||||
if swap_client.debug is True:
|
||||
swap_client.log.error(traceback.format_exc())
|
||||
else:
|
||||
swap_client.log.error(f'remove_expired_data: {e}')
|
||||
err_messages.append('Failed.')
|
||||
swap_client.log.error(f"remove_expired_data: {e}")
|
||||
err_messages.append("Failed.")
|
||||
|
||||
template = server.env.get_template('debug.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'result': result,
|
||||
'summary': summary,
|
||||
})
|
||||
template = server.env.get_template("debug.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"messages": messages,
|
||||
"err_messages": err_messages,
|
||||
"result": result,
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -17,47 +17,52 @@ def page_changepassword(self, url_split, post_string):
|
|||
messages = []
|
||||
err_messages = []
|
||||
|
||||
form_data = self.checkForm(post_string, 'changepassword', err_messages)
|
||||
form_data = self.checkForm(post_string, "changepassword", err_messages)
|
||||
if form_data:
|
||||
old_password = get_data_entry_or(form_data, 'oldpassword', '')
|
||||
new_password = get_data_entry_or(form_data, 'newpassword', '')
|
||||
confirm_password = get_data_entry_or(form_data, 'confirmpassword', '')
|
||||
old_password = get_data_entry_or(form_data, "oldpassword", "")
|
||||
new_password = get_data_entry_or(form_data, "newpassword", "")
|
||||
confirm_password = get_data_entry_or(form_data, "confirmpassword", "")
|
||||
|
||||
try:
|
||||
if new_password == '':
|
||||
raise ValueError('New password must be entered.')
|
||||
if new_password == "":
|
||||
raise ValueError("New password must be entered.")
|
||||
if new_password != confirm_password:
|
||||
raise ValueError('New password and confirm password must match.')
|
||||
raise ValueError("New password and confirm password must match.")
|
||||
swap_client.changeWalletPasswords(old_password, new_password)
|
||||
messages.append('Password changed')
|
||||
messages.append("Password changed")
|
||||
except Exception as e:
|
||||
err_messages.append(str(e))
|
||||
|
||||
template = server.env.get_template('changepassword.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'summary': swap_client.getSummary(),
|
||||
})
|
||||
template = server.env.get_template("changepassword.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"messages": messages,
|
||||
"err_messages": err_messages,
|
||||
"summary": swap_client.getSummary(),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def page_unlock(self, url_split, post_string):
|
||||
server = self.server
|
||||
swap_client = server.swap_client
|
||||
|
||||
messages = ['Warning: This will unlock the system for all users!', ]
|
||||
messages = [
|
||||
"Warning: This will unlock the system for all users!",
|
||||
]
|
||||
err_messages = []
|
||||
|
||||
form_data = self.checkForm(post_string, 'unlock', err_messages)
|
||||
form_data = self.checkForm(post_string, "unlock", err_messages)
|
||||
if form_data:
|
||||
password = get_data_entry_or(form_data, 'password', '')
|
||||
password = get_data_entry_or(form_data, "password", "")
|
||||
|
||||
try:
|
||||
if password == '':
|
||||
raise ValueError('Password must be entered.')
|
||||
if password == "":
|
||||
raise ValueError("Password must be entered.")
|
||||
swap_client.unlockWallets(password)
|
||||
self.send_response(302)
|
||||
self.send_header('Location', '/')
|
||||
self.send_header("Location", "/")
|
||||
self.end_headers()
|
||||
return bytes()
|
||||
except Exception as e:
|
||||
|
@ -65,11 +70,14 @@ def page_unlock(self, url_split, post_string):
|
|||
swap_client.log.error(str(e))
|
||||
err_messages.append(str(e))
|
||||
|
||||
template = server.env.get_template('unlock.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
})
|
||||
template = server.env.get_template("unlock.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"messages": messages,
|
||||
"err_messages": err_messages,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def page_lock(self, url_split, post_string):
|
||||
|
@ -79,6 +87,6 @@ def page_lock(self, url_split, post_string):
|
|||
|
||||
swap_client.lockWallets()
|
||||
self.send_response(302)
|
||||
self.send_header('Location', '/')
|
||||
self.send_header("Location", "/")
|
||||
self.end_headers()
|
||||
return bytes()
|
||||
|
|
|
@ -25,54 +25,71 @@ def page_identity(self, url_split, post_string):
|
|||
swap_client.checkSystemStatus()
|
||||
summary = swap_client.getSummary()
|
||||
|
||||
ensure(len(url_split) > 2, 'Address not specified')
|
||||
ensure(len(url_split) > 2, "Address not specified")
|
||||
identity_address = url_split[2]
|
||||
|
||||
page_data = {'identity_address': identity_address}
|
||||
page_data = {"identity_address": identity_address}
|
||||
messages = []
|
||||
err_messages = []
|
||||
form_data = self.checkForm(post_string, 'identity', err_messages)
|
||||
form_data = self.checkForm(post_string, "identity", err_messages)
|
||||
if form_data:
|
||||
if have_data_entry(form_data, 'edit'):
|
||||
page_data['show_edit_form'] = True
|
||||
if have_data_entry(form_data, 'apply'):
|
||||
if have_data_entry(form_data, "edit"):
|
||||
page_data["show_edit_form"] = True
|
||||
if have_data_entry(form_data, "apply"):
|
||||
try:
|
||||
data = {
|
||||
'label': get_data_entry_or(form_data, 'label', ''),
|
||||
'note': get_data_entry_or(form_data, 'note', ''),
|
||||
'automation_override': get_data_entry(form_data, 'automation_override'),
|
||||
"label": get_data_entry_or(form_data, "label", ""),
|
||||
"note": get_data_entry_or(form_data, "note", ""),
|
||||
"automation_override": get_data_entry(
|
||||
form_data, "automation_override"
|
||||
),
|
||||
}
|
||||
swap_client.setIdentityData({'address': identity_address}, data)
|
||||
messages.append('Updated')
|
||||
swap_client.setIdentityData({"address": identity_address}, data)
|
||||
messages.append("Updated")
|
||||
except Exception as e:
|
||||
err_messages.append(str(e))
|
||||
|
||||
try:
|
||||
identity = swap_client.getIdentity(identity_address)
|
||||
if identity is None:
|
||||
raise ValueError('Unknown address')
|
||||
raise ValueError("Unknown address")
|
||||
|
||||
automation_override = zeroIfNone(identity.automation_override)
|
||||
page_data.update({
|
||||
'label': '' if identity.label is None else identity.label,
|
||||
'num_sent_bids_successful': zeroIfNone(identity.num_sent_bids_successful),
|
||||
'num_recv_bids_successful': zeroIfNone(identity.num_recv_bids_successful),
|
||||
'num_sent_bids_rejected': zeroIfNone(identity.num_sent_bids_rejected),
|
||||
'num_recv_bids_rejected': zeroIfNone(identity.num_recv_bids_rejected),
|
||||
'num_sent_bids_failed': zeroIfNone(identity.num_sent_bids_failed),
|
||||
'num_recv_bids_failed': zeroIfNone(identity.num_recv_bids_failed),
|
||||
'automation_override': automation_override,
|
||||
'str_automation_override': strAutomationOverrideOption(automation_override),
|
||||
'note': '' if identity.note is None else identity.note,
|
||||
})
|
||||
page_data.update(
|
||||
{
|
||||
"label": "" if identity.label is None else identity.label,
|
||||
"num_sent_bids_successful": zeroIfNone(
|
||||
identity.num_sent_bids_successful
|
||||
),
|
||||
"num_recv_bids_successful": zeroIfNone(
|
||||
identity.num_recv_bids_successful
|
||||
),
|
||||
"num_sent_bids_rejected": zeroIfNone(identity.num_sent_bids_rejected),
|
||||
"num_recv_bids_rejected": zeroIfNone(identity.num_recv_bids_rejected),
|
||||
"num_sent_bids_failed": zeroIfNone(identity.num_sent_bids_failed),
|
||||
"num_recv_bids_failed": zeroIfNone(identity.num_recv_bids_failed),
|
||||
"automation_override": automation_override,
|
||||
"str_automation_override": strAutomationOverrideOption(
|
||||
automation_override
|
||||
),
|
||||
"note": "" if identity.note is None else identity.note,
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
err_messages.append(e)
|
||||
|
||||
template = server.env.get_template('identity.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'data': page_data,
|
||||
'automation_override_options': [(int(opt), strAutomationOverrideOption(opt)) for opt in AutomationOverrideOptions if opt > 0],
|
||||
'summary': summary,
|
||||
})
|
||||
template = server.env.get_template("identity.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"messages": messages,
|
||||
"err_messages": err_messages,
|
||||
"data": page_data,
|
||||
"automation_override_options": [
|
||||
(int(opt), strAutomationOverrideOption(opt))
|
||||
for opt in AutomationOverrideOptions
|
||||
if opt > 0
|
||||
],
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -28,140 +28,195 @@ def page_settings(self, url_split, post_string):
|
|||
|
||||
messages = []
|
||||
err_messages = []
|
||||
active_tab = 'default'
|
||||
form_data = self.checkForm(post_string, 'settings', err_messages)
|
||||
active_tab = "default"
|
||||
form_data = self.checkForm(post_string, "settings", err_messages)
|
||||
if form_data:
|
||||
try:
|
||||
if have_data_entry(form_data, 'apply_general'):
|
||||
active_tab = 'general'
|
||||
if have_data_entry(form_data, "apply_general"):
|
||||
active_tab = "general"
|
||||
data = {
|
||||
'debug': toBool(get_data_entry(form_data, 'debugmode')),
|
||||
'debug_ui': toBool(get_data_entry(form_data, 'debugui')),
|
||||
'expire_db_records': toBool(get_data_entry(form_data, 'expire_db_records')),
|
||||
"debug": toBool(get_data_entry(form_data, "debugmode")),
|
||||
"debug_ui": toBool(get_data_entry(form_data, "debugui")),
|
||||
"expire_db_records": toBool(
|
||||
get_data_entry(form_data, "expire_db_records")
|
||||
),
|
||||
}
|
||||
swap_client.editGeneralSettings(data)
|
||||
elif have_data_entry(form_data, 'apply_chart'):
|
||||
active_tab = 'general'
|
||||
elif have_data_entry(form_data, "apply_chart"):
|
||||
active_tab = "general"
|
||||
data = {
|
||||
'show_chart': toBool(get_data_entry(form_data, 'showchart')),
|
||||
'chart_api_key': html.unescape(get_data_entry_or(form_data, 'chartapikey', '')),
|
||||
'coingecko_api_key': html.unescape(get_data_entry_or(form_data, 'coingeckoapikey', '')),
|
||||
'enabled_chart_coins': get_data_entry_or(form_data, 'enabledchartcoins', ''),
|
||||
"show_chart": toBool(get_data_entry(form_data, "showchart")),
|
||||
"chart_api_key": html.unescape(
|
||||
get_data_entry_or(form_data, "chartapikey", "")
|
||||
),
|
||||
"coingecko_api_key": html.unescape(
|
||||
get_data_entry_or(form_data, "coingeckoapikey", "")
|
||||
),
|
||||
"enabled_chart_coins": get_data_entry_or(
|
||||
form_data, "enabledchartcoins", ""
|
||||
),
|
||||
}
|
||||
swap_client.editGeneralSettings(data)
|
||||
elif have_data_entry(form_data, 'apply_tor'):
|
||||
active_tab = 'tor'
|
||||
elif have_data_entry(form_data, "apply_tor"):
|
||||
active_tab = "tor"
|
||||
# TODO: Detect if running in docker
|
||||
raise ValueError('TODO: If running in docker see doc/tor.md to enable/disable tor.')
|
||||
raise ValueError(
|
||||
"TODO: If running in docker see doc/tor.md to enable/disable tor."
|
||||
)
|
||||
|
||||
for name, c in swap_client.settings['chainclients'].items():
|
||||
if have_data_entry(form_data, 'apply_' + name):
|
||||
data = {'lookups': get_data_entry(form_data, 'lookups_' + name)}
|
||||
if name in ('monero', 'wownero'):
|
||||
data['fee_priority'] = int(get_data_entry(form_data, 'fee_priority_' + name))
|
||||
data['manage_daemon'] = True if get_data_entry(form_data, 'managedaemon_' + name) == 'true' else False
|
||||
data['rpchost'] = get_data_entry(form_data, 'rpchost_' + name)
|
||||
data['rpcport'] = int(get_data_entry(form_data, 'rpcport_' + name))
|
||||
data['remotedaemonurls'] = get_data_entry_or(form_data, 'remotedaemonurls_' + name, '')
|
||||
data['automatically_select_daemon'] = True if get_data_entry(form_data, 'autosetdaemon_' + name) == 'true' else False
|
||||
for name, c in swap_client.settings["chainclients"].items():
|
||||
if have_data_entry(form_data, "apply_" + name):
|
||||
data = {"lookups": get_data_entry(form_data, "lookups_" + name)}
|
||||
if name in ("monero", "wownero"):
|
||||
data["fee_priority"] = int(
|
||||
get_data_entry(form_data, "fee_priority_" + name)
|
||||
)
|
||||
data["manage_daemon"] = (
|
||||
True
|
||||
if get_data_entry(form_data, "managedaemon_" + name)
|
||||
== "true"
|
||||
else False
|
||||
)
|
||||
data["rpchost"] = get_data_entry(form_data, "rpchost_" + name)
|
||||
data["rpcport"] = int(
|
||||
get_data_entry(form_data, "rpcport_" + name)
|
||||
)
|
||||
data["remotedaemonurls"] = get_data_entry_or(
|
||||
form_data, "remotedaemonurls_" + name, ""
|
||||
)
|
||||
data["automatically_select_daemon"] = (
|
||||
True
|
||||
if get_data_entry(form_data, "autosetdaemon_" + name)
|
||||
== "true"
|
||||
else False
|
||||
)
|
||||
else:
|
||||
data['conf_target'] = int(get_data_entry(form_data, 'conf_target_' + name))
|
||||
if name == 'particl':
|
||||
data['anon_tx_ring_size'] = int(get_data_entry(form_data, 'rct_ring_size_' + name))
|
||||
data["conf_target"] = int(
|
||||
get_data_entry(form_data, "conf_target_" + name)
|
||||
)
|
||||
if name == "particl":
|
||||
data["anon_tx_ring_size"] = int(
|
||||
get_data_entry(form_data, "rct_ring_size_" + name)
|
||||
)
|
||||
|
||||
settings_changed, suggest_reboot = swap_client.editSettings(name, data)
|
||||
settings_changed, suggest_reboot = swap_client.editSettings(
|
||||
name, data
|
||||
)
|
||||
if settings_changed is True:
|
||||
messages.append('Settings applied.')
|
||||
messages.append("Settings applied.")
|
||||
if suggest_reboot is True:
|
||||
messages.append('Please restart BasicSwap.')
|
||||
elif have_data_entry(form_data, 'enable_' + name):
|
||||
messages.append("Please restart BasicSwap.")
|
||||
elif have_data_entry(form_data, "enable_" + name):
|
||||
swap_client.enableCoin(name)
|
||||
display_name = getCoinName(swap_client.getCoinIdFromName(name))
|
||||
messages.append(display_name + ' enabled, shutting down.')
|
||||
messages.append(display_name + " enabled, shutting down.")
|
||||
swap_client.stopRunning()
|
||||
elif have_data_entry(form_data, 'disable_' + name):
|
||||
elif have_data_entry(form_data, "disable_" + name):
|
||||
swap_client.disableCoin(name)
|
||||
display_name = getCoinName(swap_client.getCoinIdFromName(name))
|
||||
messages.append(display_name + ' disabled, shutting down.')
|
||||
messages.append(display_name + " disabled, shutting down.")
|
||||
swap_client.stopRunning()
|
||||
except InactiveCoin as ex:
|
||||
err_messages.append('InactiveCoin {}'.format(Coins(ex.coinid).name))
|
||||
err_messages.append("InactiveCoin {}".format(Coins(ex.coinid).name))
|
||||
except Exception as e:
|
||||
err_messages.append(str(e))
|
||||
chains_formatted = []
|
||||
|
||||
sorted_names = sorted(swap_client.settings['chainclients'].keys())
|
||||
sorted_names = sorted(swap_client.settings["chainclients"].keys())
|
||||
for name in sorted_names:
|
||||
c = swap_client.settings['chainclients'][name]
|
||||
c = swap_client.settings["chainclients"][name]
|
||||
try:
|
||||
display_name = getCoinName(swap_client.getCoinIdFromName(name))
|
||||
except Exception:
|
||||
display_name = name
|
||||
chains_formatted.append({
|
||||
'name': name,
|
||||
'display_name': display_name,
|
||||
'lookups': c.get('chain_lookups', 'local'),
|
||||
'manage_daemon': c.get('manage_daemon', 'Unknown'),
|
||||
'connection_type': c.get('connection_type', 'Unknown'),
|
||||
})
|
||||
if name in ('monero', 'wownero'):
|
||||
chains_formatted[-1]['fee_priority'] = c.get('fee_priority', 0)
|
||||
chains_formatted[-1]['manage_wallet_daemon'] = c.get('manage_wallet_daemon', 'Unknown')
|
||||
chains_formatted[-1]['rpchost'] = c.get('rpchost', 'localhost')
|
||||
chains_formatted[-1]['rpcport'] = int(c.get('rpcport', 18081))
|
||||
chains_formatted[-1]['remotedaemonurls'] = '\n'.join(c.get('remote_daemon_urls', []))
|
||||
chains_formatted[-1]['autosetdaemon'] = c.get('automatically_select_daemon', False)
|
||||
chains_formatted.append(
|
||||
{
|
||||
"name": name,
|
||||
"display_name": display_name,
|
||||
"lookups": c.get("chain_lookups", "local"),
|
||||
"manage_daemon": c.get("manage_daemon", "Unknown"),
|
||||
"connection_type": c.get("connection_type", "Unknown"),
|
||||
}
|
||||
)
|
||||
if name in ("monero", "wownero"):
|
||||
chains_formatted[-1]["fee_priority"] = c.get("fee_priority", 0)
|
||||
chains_formatted[-1]["manage_wallet_daemon"] = c.get(
|
||||
"manage_wallet_daemon", "Unknown"
|
||||
)
|
||||
chains_formatted[-1]["rpchost"] = c.get("rpchost", "localhost")
|
||||
chains_formatted[-1]["rpcport"] = int(c.get("rpcport", 18081))
|
||||
chains_formatted[-1]["remotedaemonurls"] = "\n".join(
|
||||
c.get("remote_daemon_urls", [])
|
||||
)
|
||||
chains_formatted[-1]["autosetdaemon"] = c.get(
|
||||
"automatically_select_daemon", False
|
||||
)
|
||||
else:
|
||||
chains_formatted[-1]['conf_target'] = c.get('conf_target', 2)
|
||||
chains_formatted[-1]["conf_target"] = c.get("conf_target", 2)
|
||||
|
||||
if name == 'particl':
|
||||
chains_formatted[-1]['anon_tx_ring_size'] = c.get('anon_tx_ring_size', 12)
|
||||
if name == "particl":
|
||||
chains_formatted[-1]["anon_tx_ring_size"] = c.get("anon_tx_ring_size", 12)
|
||||
else:
|
||||
if c.get('connection_type', 'Unknown') == 'none':
|
||||
if 'connection_type_prev' in c:
|
||||
chains_formatted[-1]['can_reenable'] = True
|
||||
if c.get("connection_type", "Unknown") == "none":
|
||||
if "connection_type_prev" in c:
|
||||
chains_formatted[-1]["can_reenable"] = True
|
||||
else:
|
||||
chains_formatted[-1]['can_disable'] = True
|
||||
chains_formatted[-1]["can_disable"] = True
|
||||
|
||||
general_settings = {
|
||||
'debug': swap_client.debug,
|
||||
'debug_ui': swap_client.debug_ui,
|
||||
'expire_db_records': swap_client._expire_db_records,
|
||||
"debug": swap_client.debug,
|
||||
"debug_ui": swap_client.debug_ui,
|
||||
"expire_db_records": swap_client._expire_db_records,
|
||||
}
|
||||
if 'chart_api_key_enc' in swap_client.settings:
|
||||
chart_api_key = html.escape(bytes.fromhex(swap_client.settings.get('chart_api_key_enc', '')).decode('utf-8'))
|
||||
if "chart_api_key_enc" in swap_client.settings:
|
||||
chart_api_key = html.escape(
|
||||
bytes.fromhex(swap_client.settings.get("chart_api_key_enc", "")).decode(
|
||||
"utf-8"
|
||||
)
|
||||
)
|
||||
else:
|
||||
chart_api_key = swap_client.settings.get('chart_api_key', '')
|
||||
chart_api_key = swap_client.settings.get("chart_api_key", "")
|
||||
|
||||
if 'coingecko_api_key_enc' in swap_client.settings:
|
||||
coingecko_api_key = html.escape(bytes.fromhex(swap_client.settings.get('coingecko_api_key_enc', '')).decode('utf-8'))
|
||||
if "coingecko_api_key_enc" in swap_client.settings:
|
||||
coingecko_api_key = html.escape(
|
||||
bytes.fromhex(swap_client.settings.get("coingecko_api_key_enc", "")).decode(
|
||||
"utf-8"
|
||||
)
|
||||
)
|
||||
else:
|
||||
coingecko_api_key = swap_client.settings.get('coingecko_api_key', '')
|
||||
coingecko_api_key = swap_client.settings.get("coingecko_api_key", "")
|
||||
|
||||
chart_settings = {
|
||||
'show_chart': swap_client.settings.get('show_chart', True),
|
||||
'chart_api_key': chart_api_key,
|
||||
'coingecko_api_key': coingecko_api_key,
|
||||
'enabled_chart_coins': swap_client.settings.get('enabled_chart_coins', ''),
|
||||
"show_chart": swap_client.settings.get("show_chart", True),
|
||||
"chart_api_key": chart_api_key,
|
||||
"coingecko_api_key": coingecko_api_key,
|
||||
"enabled_chart_coins": swap_client.settings.get("enabled_chart_coins", ""),
|
||||
}
|
||||
|
||||
tor_control_password = '' if swap_client.tor_control_password is None else swap_client.tor_control_password
|
||||
tor_control_password = (
|
||||
""
|
||||
if swap_client.tor_control_password is None
|
||||
else swap_client.tor_control_password
|
||||
)
|
||||
tor_settings = {
|
||||
'use_tor': swap_client.use_tor_proxy,
|
||||
'proxy_host': swap_client.tor_proxy_host,
|
||||
'proxy_port': swap_client.tor_proxy_port,
|
||||
'control_password': html.escape(tor_control_password),
|
||||
'control_port': swap_client.tor_control_port,
|
||||
"use_tor": swap_client.use_tor_proxy,
|
||||
"proxy_host": swap_client.tor_proxy_host,
|
||||
"proxy_port": swap_client.tor_proxy_port,
|
||||
"control_password": html.escape(tor_control_password),
|
||||
"control_port": swap_client.tor_control_port,
|
||||
}
|
||||
|
||||
template = server.env.get_template('settings.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'summary': swap_client.getSummary(),
|
||||
'chains': chains_formatted,
|
||||
'general_settings': general_settings,
|
||||
'chart_settings': chart_settings,
|
||||
'tor_settings': tor_settings,
|
||||
'active_tab': active_tab,
|
||||
})
|
||||
template = server.env.get_template("settings.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"messages": messages,
|
||||
"err_messages": err_messages,
|
||||
"summary": swap_client.getSummary(),
|
||||
"chains": chains_formatted,
|
||||
"general_settings": general_settings,
|
||||
"chart_settings": chart_settings,
|
||||
"tor_settings": tor_settings,
|
||||
"active_tab": active_tab,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -28,11 +28,11 @@ def page_smsgaddresses(self, url_split, post_string):
|
|||
summary = swap_client.getSummary()
|
||||
|
||||
filters = {
|
||||
'page_no': 1,
|
||||
'limit': PAGE_LIMIT,
|
||||
'sort_by': 'created_at',
|
||||
'sort_dir': 'desc',
|
||||
'addr_type': -1,
|
||||
"page_no": 1,
|
||||
"limit": PAGE_LIMIT,
|
||||
"sort_by": "created_at",
|
||||
"sort_dir": "desc",
|
||||
"addr_type": -1,
|
||||
}
|
||||
|
||||
page_data = {}
|
||||
|
@ -41,96 +41,114 @@ def page_smsgaddresses(self, url_split, post_string):
|
|||
smsgaddresses = []
|
||||
|
||||
listaddresses = True
|
||||
form_data = self.checkForm(post_string, 'smsgaddresses', err_messages)
|
||||
form_data = self.checkForm(post_string, "smsgaddresses", err_messages)
|
||||
if form_data:
|
||||
edit_address_id = None
|
||||
for key in form_data:
|
||||
if key.startswith(b'editaddr_'):
|
||||
edit_address_id = int(key.split(b'_')[1])
|
||||
if key.startswith(b"editaddr_"):
|
||||
edit_address_id = int(key.split(b"_")[1])
|
||||
break
|
||||
if edit_address_id is not None:
|
||||
listaddresses = False
|
||||
page_data['edit_address'] = edit_address_id
|
||||
page_data['addr_data'] = swap_client.listAllSMSGAddresses({'addr_id': edit_address_id})[0]
|
||||
elif have_data_entry(form_data, 'saveaddr'):
|
||||
edit_address_id = int(get_data_entry(form_data, 'edit_address_id'))
|
||||
edit_addr = get_data_entry(form_data, 'edit_address')
|
||||
active_ind = int(get_data_entry(form_data, 'active_ind'))
|
||||
ensure(active_ind in (0, 1), 'Invalid sort by')
|
||||
addressnote = get_data_entry_or(form_data, 'addressnote', '')
|
||||
if not validateTextInput(addressnote, 'Address note', err_messages, max_length=30):
|
||||
page_data["edit_address"] = edit_address_id
|
||||
page_data["addr_data"] = swap_client.listAllSMSGAddresses(
|
||||
{"addr_id": edit_address_id}
|
||||
)[0]
|
||||
elif have_data_entry(form_data, "saveaddr"):
|
||||
edit_address_id = int(get_data_entry(form_data, "edit_address_id"))
|
||||
edit_addr = get_data_entry(form_data, "edit_address")
|
||||
active_ind = int(get_data_entry(form_data, "active_ind"))
|
||||
ensure(active_ind in (0, 1), "Invalid sort by")
|
||||
addressnote = get_data_entry_or(form_data, "addressnote", "")
|
||||
if not validateTextInput(
|
||||
addressnote, "Address note", err_messages, max_length=30
|
||||
):
|
||||
listaddresses = False
|
||||
page_data['edit_address'] = edit_address_id
|
||||
page_data["edit_address"] = edit_address_id
|
||||
else:
|
||||
swap_client.editSMSGAddress(edit_addr, active_ind=active_ind, addressnote=addressnote)
|
||||
messages.append(f'Edited address {edit_addr}')
|
||||
elif have_data_entry(form_data, 'shownewaddr'):
|
||||
swap_client.editSMSGAddress(
|
||||
edit_addr, active_ind=active_ind, addressnote=addressnote
|
||||
)
|
||||
messages.append(f"Edited address {edit_addr}")
|
||||
elif have_data_entry(form_data, "shownewaddr"):
|
||||
listaddresses = False
|
||||
page_data['new_address'] = True
|
||||
elif have_data_entry(form_data, 'showaddaddr'):
|
||||
page_data["new_address"] = True
|
||||
elif have_data_entry(form_data, "showaddaddr"):
|
||||
listaddresses = False
|
||||
page_data['new_send_address'] = True
|
||||
elif have_data_entry(form_data, 'createnewaddr'):
|
||||
addressnote = get_data_entry_or(form_data, 'addressnote', '')
|
||||
if not validateTextInput(addressnote, 'Address note', err_messages, max_length=30):
|
||||
page_data["new_send_address"] = True
|
||||
elif have_data_entry(form_data, "createnewaddr"):
|
||||
addressnote = get_data_entry_or(form_data, "addressnote", "")
|
||||
if not validateTextInput(
|
||||
addressnote, "Address note", err_messages, max_length=30
|
||||
):
|
||||
listaddresses = False
|
||||
page_data['new_address'] = True
|
||||
page_data["new_address"] = True
|
||||
else:
|
||||
new_addr, pubkey = swap_client.newSMSGAddress(addressnote=addressnote)
|
||||
messages.append(f'Created address {new_addr}, pubkey {pubkey}')
|
||||
elif have_data_entry(form_data, 'createnewsendaddr'):
|
||||
pubkey_hex = get_data_entry(form_data, 'addresspubkey')
|
||||
addressnote = get_data_entry_or(form_data, 'addressnote', '')
|
||||
if not validateTextInput(addressnote, 'Address note', messages, max_length=30) or \
|
||||
not validateTextInput(pubkey_hex, 'Pubkey', messages, max_length=66):
|
||||
messages.append(f"Created address {new_addr}, pubkey {pubkey}")
|
||||
elif have_data_entry(form_data, "createnewsendaddr"):
|
||||
pubkey_hex = get_data_entry(form_data, "addresspubkey")
|
||||
addressnote = get_data_entry_or(form_data, "addressnote", "")
|
||||
if not validateTextInput(
|
||||
addressnote, "Address note", messages, max_length=30
|
||||
) or not validateTextInput(pubkey_hex, "Pubkey", messages, max_length=66):
|
||||
listaddresses = False
|
||||
page_data['new_send_address'] = True
|
||||
page_data["new_send_address"] = True
|
||||
else:
|
||||
new_addr = swap_client.addSMSGAddress(pubkey_hex, addressnote=addressnote)
|
||||
messages.append(f'Added address {new_addr}')
|
||||
new_addr = swap_client.addSMSGAddress(
|
||||
pubkey_hex, addressnote=addressnote
|
||||
)
|
||||
messages.append(f"Added address {new_addr}")
|
||||
|
||||
if have_data_entry(form_data, 'clearfilters'):
|
||||
swap_client.clearFilters('page_smsgaddresses')
|
||||
if have_data_entry(form_data, "clearfilters"):
|
||||
swap_client.clearFilters("page_smsgaddresses")
|
||||
else:
|
||||
if have_data_entry(form_data, 'sort_by'):
|
||||
sort_by = get_data_entry(form_data, 'sort_by')
|
||||
ensure(sort_by in ['created_at', 'rate'], 'Invalid sort by')
|
||||
filters['sort_by'] = sort_by
|
||||
if have_data_entry(form_data, 'sort_dir'):
|
||||
sort_dir = get_data_entry(form_data, 'sort_dir')
|
||||
ensure(sort_dir in ['asc', 'desc'], 'Invalid sort dir')
|
||||
filters['sort_dir'] = sort_dir
|
||||
if have_data_entry(form_data, 'filter_addressnote'):
|
||||
addressnote = get_data_entry(form_data, 'filter_addressnote')
|
||||
if validateTextInput(addressnote, 'Address note', err_messages, max_length=30):
|
||||
filters['addressnote'] = addressnote
|
||||
if have_data_entry(form_data, 'filter_addr_type'):
|
||||
filters['addr_type'] = int(get_data_entry(form_data, 'filter_addr_type'))
|
||||
if have_data_entry(form_data, "sort_by"):
|
||||
sort_by = get_data_entry(form_data, "sort_by")
|
||||
ensure(sort_by in ["created_at", "rate"], "Invalid sort by")
|
||||
filters["sort_by"] = sort_by
|
||||
if have_data_entry(form_data, "sort_dir"):
|
||||
sort_dir = get_data_entry(form_data, "sort_dir")
|
||||
ensure(sort_dir in ["asc", "desc"], "Invalid sort dir")
|
||||
filters["sort_dir"] = sort_dir
|
||||
if have_data_entry(form_data, "filter_addressnote"):
|
||||
addressnote = get_data_entry(form_data, "filter_addressnote")
|
||||
if validateTextInput(
|
||||
addressnote, "Address note", err_messages, max_length=30
|
||||
):
|
||||
filters["addressnote"] = addressnote
|
||||
if have_data_entry(form_data, "filter_addr_type"):
|
||||
filters["addr_type"] = int(
|
||||
get_data_entry(form_data, "filter_addr_type")
|
||||
)
|
||||
|
||||
set_pagination_filters(form_data, filters)
|
||||
if have_data_entry(form_data, 'applyfilters'):
|
||||
swap_client.setFilters('page_smsgaddresses', filters)
|
||||
if have_data_entry(form_data, "applyfilters"):
|
||||
swap_client.setFilters("page_smsgaddresses", filters)
|
||||
else:
|
||||
saved_filters = swap_client.getFilters('page_smsgaddresses')
|
||||
saved_filters = swap_client.getFilters("page_smsgaddresses")
|
||||
if saved_filters:
|
||||
filters.update(saved_filters)
|
||||
|
||||
if listaddresses is True:
|
||||
smsgaddresses = swap_client.listAllSMSGAddresses(filters)
|
||||
|
||||
page_data['addr_types'] = [(int(t), strAddressType(t)) for t in AddressTypes]
|
||||
page_data['network_addr'] = swap_client.network_addr
|
||||
page_data["addr_types"] = [(int(t), strAddressType(t)) for t in AddressTypes]
|
||||
page_data["network_addr"] = swap_client.network_addr
|
||||
|
||||
for addr in smsgaddresses:
|
||||
addr['type'] = strAddressType(addr['type'])
|
||||
addr["type"] = strAddressType(addr["type"])
|
||||
|
||||
template = self.server.env.get_template('smsgaddresses.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'filters': filters,
|
||||
'data': page_data,
|
||||
'smsgaddresses': smsgaddresses,
|
||||
'page_data': page_data,
|
||||
'summary': summary,
|
||||
})
|
||||
template = self.server.env.get_template("smsgaddresses.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"messages": messages,
|
||||
"err_messages": err_messages,
|
||||
"filters": filters,
|
||||
"data": page_data,
|
||||
"smsgaddresses": smsgaddresses,
|
||||
"page_data": page_data,
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -7,19 +7,19 @@
|
|||
def extract_data(bytes_in):
|
||||
if bytes_in is None:
|
||||
return None
|
||||
str_in = bytes_in.decode('utf-8')
|
||||
start = str_in.find('=')
|
||||
str_in = bytes_in.decode("utf-8")
|
||||
start = str_in.find("=")
|
||||
if start < 0:
|
||||
return None
|
||||
start += 1
|
||||
end = str_in.find('\r', start)
|
||||
end = str_in.find("\r", start)
|
||||
if end < 0:
|
||||
return None
|
||||
return str_in[start: end]
|
||||
return str_in[start:end]
|
||||
|
||||
|
||||
def get_tor_established_state(swap_client):
|
||||
rv = swap_client.torControl('GETINFO status/circuit-established')
|
||||
rv = swap_client.torControl("GETINFO status/circuit-established")
|
||||
return extract_data(rv)
|
||||
|
||||
|
||||
|
@ -28,23 +28,26 @@ def page_tor(self, url_split, post_string):
|
|||
summary = swap_client.getSummary()
|
||||
page_data = {}
|
||||
try:
|
||||
page_data['circuit_established'] = get_tor_established_state(swap_client)
|
||||
page_data["circuit_established"] = get_tor_established_state(swap_client)
|
||||
except Exception:
|
||||
page_data['circuit_established'] = 'error'
|
||||
page_data["circuit_established"] = "error"
|
||||
try:
|
||||
rv = swap_client.torControl('GETINFO traffic/read')
|
||||
page_data['bytes_written'] = extract_data(rv)
|
||||
rv = swap_client.torControl("GETINFO traffic/read")
|
||||
page_data["bytes_written"] = extract_data(rv)
|
||||
except Exception:
|
||||
page_data['bytes_written'] = 'error'
|
||||
page_data["bytes_written"] = "error"
|
||||
try:
|
||||
rv = swap_client.torControl('GETINFO traffic/written')
|
||||
page_data['bytes_read'] = extract_data(rv)
|
||||
rv = swap_client.torControl("GETINFO traffic/written")
|
||||
page_data["bytes_read"] = extract_data(rv)
|
||||
except Exception:
|
||||
page_data['bytes_read'] = 'error'
|
||||
page_data["bytes_read"] = "error"
|
||||
messages = []
|
||||
template = self.server.env.get_template('tor.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'data': page_data,
|
||||
'summary': summary,
|
||||
})
|
||||
template = self.server.env.get_template("tor.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"messages": messages,
|
||||
"data": page_data,
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022-2023 tecnovert
|
||||
# Copyright (c) 2024 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -23,52 +24,52 @@ from basicswap.chainparams import (
|
|||
|
||||
def format_wallet_data(swap_client, ci, w):
|
||||
wf = {
|
||||
'name': ci.coin_name(),
|
||||
'version': w.get('version', '?'),
|
||||
'ticker': ci.ticker_mainnet(),
|
||||
'cid': str(int(ci.coin_type())),
|
||||
'balance': w.get('balance', '?'),
|
||||
'blocks': w.get('blocks', '?'),
|
||||
'synced': w.get('synced', '?'),
|
||||
'expected_seed': w.get('expected_seed', '?'),
|
||||
'encrypted': w.get('encrypted', '?'),
|
||||
'locked': w.get('locked', '?'),
|
||||
'updating': w.get('updating', '?'),
|
||||
'havedata': True,
|
||||
"name": ci.coin_name(),
|
||||
"version": w.get("version", "?"),
|
||||
"ticker": ci.ticker_mainnet(),
|
||||
"cid": str(int(ci.coin_type())),
|
||||
"balance": w.get("balance", "?"),
|
||||
"blocks": w.get("blocks", "?"),
|
||||
"synced": w.get("synced", "?"),
|
||||
"expected_seed": w.get("expected_seed", "?"),
|
||||
"encrypted": w.get("encrypted", "?"),
|
||||
"locked": w.get("locked", "?"),
|
||||
"updating": w.get("updating", "?"),
|
||||
"havedata": True,
|
||||
}
|
||||
|
||||
if w.get('bootstrapping', False) is True:
|
||||
wf['bootstrapping'] = True
|
||||
if 'known_block_count' in w:
|
||||
wf['known_block_count'] = w['known_block_count']
|
||||
if 'locked_utxos' in w:
|
||||
wf['locked_utxos'] = w['locked_utxos']
|
||||
if w.get("bootstrapping", False) is True:
|
||||
wf["bootstrapping"] = True
|
||||
if "known_block_count" in w:
|
||||
wf["known_block_count"] = w["known_block_count"]
|
||||
if "locked_utxos" in w:
|
||||
wf["locked_utxos"] = w["locked_utxos"]
|
||||
|
||||
if 'balance' in w and 'unconfirmed' in w:
|
||||
wf['balance_all'] = float(w['balance']) + float(w['unconfirmed'])
|
||||
if 'lastupdated' in w:
|
||||
wf['lastupdated'] = format_timestamp(w['lastupdated'])
|
||||
if "balance" in w and "unconfirmed" in w:
|
||||
wf["balance_all"] = float(w["balance"]) + float(w["unconfirmed"])
|
||||
if "lastupdated" in w:
|
||||
wf["lastupdated"] = format_timestamp(w["lastupdated"])
|
||||
|
||||
pending: int = 0
|
||||
if 'unconfirmed' in w and float(w['unconfirmed']) > 0.0:
|
||||
pending += ci.make_int(w['unconfirmed'])
|
||||
if 'immature' in w and float(w['immature']) > 0.0:
|
||||
pending += ci.make_int(w['immature'])
|
||||
if "unconfirmed" in w and float(w["unconfirmed"]) > 0.0:
|
||||
pending += ci.make_int(w["unconfirmed"])
|
||||
if "immature" in w and float(w["immature"]) > 0.0:
|
||||
pending += ci.make_int(w["immature"])
|
||||
if pending > 0.0:
|
||||
wf['pending'] = ci.format_amount(pending)
|
||||
wf["pending"] = ci.format_amount(pending)
|
||||
|
||||
if ci.coin_type() == Coins.PART:
|
||||
wf['stealth_address'] = w.get('stealth_address', '?')
|
||||
wf['blind_balance'] = w.get('blind_balance', '?')
|
||||
if 'blind_unconfirmed' in w and float(w['blind_unconfirmed']) > 0.0:
|
||||
wf['blind_unconfirmed'] = w['blind_unconfirmed']
|
||||
wf['anon_balance'] = w.get('anon_balance', '?')
|
||||
if 'anon_pending' in w and float(w['anon_pending']) > 0.0:
|
||||
wf['anon_pending'] = w['anon_pending']
|
||||
wf["stealth_address"] = w.get("stealth_address", "?")
|
||||
wf["blind_balance"] = w.get("blind_balance", "?")
|
||||
if "blind_unconfirmed" in w and float(w["blind_unconfirmed"]) > 0.0:
|
||||
wf["blind_unconfirmed"] = w["blind_unconfirmed"]
|
||||
wf["anon_balance"] = w.get("anon_balance", "?")
|
||||
if "anon_pending" in w and float(w["anon_pending"]) > 0.0:
|
||||
wf["anon_pending"] = w["anon_pending"]
|
||||
elif ci.coin_type() == Coins.LTC:
|
||||
wf['mweb_address'] = w.get('mweb_address', '?')
|
||||
wf['mweb_balance'] = w.get('mweb_balance', '?')
|
||||
wf['mweb_pending'] = w.get('mweb_pending', '?')
|
||||
wf["mweb_address"] = w.get("mweb_address", "?")
|
||||
wf["mweb_balance"] = w.get("mweb_balance", "?")
|
||||
wf["mweb_pending"] = w.get("mweb_pending", "?")
|
||||
|
||||
checkAddressesOwned(swap_client, ci, wf)
|
||||
return wf
|
||||
|
@ -91,19 +92,18 @@ def page_wallets(self, url_split, post_string):
|
|||
|
||||
for k in sk:
|
||||
w = wallets[k]
|
||||
if 'error' in w:
|
||||
wallets_formatted.append({
|
||||
'cid': str(int(k)),
|
||||
'error': w['error']
|
||||
})
|
||||
if "error" in w:
|
||||
wallets_formatted.append({"cid": str(int(k)), "error": w["error"]})
|
||||
continue
|
||||
|
||||
if 'no_data' in w:
|
||||
wallets_formatted.append({
|
||||
'name': w['name'],
|
||||
'havedata': False,
|
||||
'updating': w['updating'],
|
||||
})
|
||||
if "no_data" in w:
|
||||
wallets_formatted.append(
|
||||
{
|
||||
"name": w["name"],
|
||||
"havedata": False,
|
||||
"updating": w["updating"],
|
||||
}
|
||||
)
|
||||
continue
|
||||
|
||||
ci = swap_client.ci(k)
|
||||
|
@ -111,17 +111,20 @@ def page_wallets(self, url_split, post_string):
|
|||
|
||||
wallets_formatted.append(wf)
|
||||
|
||||
template = server.env.get_template('wallets.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'wallets': wallets_formatted,
|
||||
'summary': summary,
|
||||
})
|
||||
template = server.env.get_template("wallets.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"messages": messages,
|
||||
"err_messages": err_messages,
|
||||
"wallets": wallets_formatted,
|
||||
"summary": summary,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def page_wallet(self, url_split, post_string):
|
||||
ensure(len(url_split) > 2, 'Wallet not specified')
|
||||
ensure(len(url_split) > 2, "Wallet not specified")
|
||||
wallet_ticker = url_split[2]
|
||||
server = self.server
|
||||
swap_client = server.swap_client
|
||||
|
@ -136,94 +139,130 @@ def page_wallet(self, url_split, post_string):
|
|||
show_utxo_groups: bool = False
|
||||
withdrawal_successful: bool = False
|
||||
force_refresh: bool = False
|
||||
form_data = self.checkForm(post_string, 'wallet', err_messages)
|
||||
form_data = self.checkForm(post_string, "wallet", err_messages)
|
||||
if form_data:
|
||||
cid = str(int(coin_id))
|
||||
|
||||
estimate_fee: bool = have_data_entry(form_data, 'estfee_' + cid)
|
||||
withdraw: bool = have_data_entry(form_data, 'withdraw_' + cid)
|
||||
if have_data_entry(form_data, 'newaddr_' + cid):
|
||||
estimate_fee: bool = have_data_entry(form_data, "estfee_" + cid)
|
||||
withdraw: bool = have_data_entry(form_data, "withdraw_" + cid)
|
||||
if have_data_entry(form_data, "newaddr_" + cid):
|
||||
swap_client.cacheNewAddressForCoin(coin_id)
|
||||
elif have_data_entry(form_data, 'forcerefresh'):
|
||||
elif have_data_entry(form_data, "forcerefresh"):
|
||||
force_refresh = True
|
||||
elif have_data_entry(form_data, 'newmwebaddr_' + cid):
|
||||
elif have_data_entry(form_data, "newmwebaddr_" + cid):
|
||||
swap_client.cacheNewStealthAddressForCoin(coin_id)
|
||||
elif have_data_entry(form_data, 'reseed_' + cid):
|
||||
elif have_data_entry(form_data, "reseed_" + cid):
|
||||
try:
|
||||
swap_client.reseedWallet(coin_id)
|
||||
messages.append('Reseed complete ' + str(coin_id))
|
||||
messages.append("Reseed complete " + str(coin_id))
|
||||
except Exception as ex:
|
||||
err_messages.append('Reseed failed ' + str(ex))
|
||||
err_messages.append("Reseed failed " + str(ex))
|
||||
swap_client.updateWalletsInfo(True, coin_id)
|
||||
elif withdraw or estimate_fee:
|
||||
subfee = True if have_data_entry(form_data, 'subfee_' + cid) else False
|
||||
page_data['wd_subfee_' + cid] = subfee
|
||||
subfee = True if have_data_entry(form_data, "subfee_" + cid) else False
|
||||
page_data["wd_subfee_" + cid] = subfee
|
||||
|
||||
sweepall = True if have_data_entry(form_data, 'sweepall_' + cid) else False
|
||||
page_data['wd_sweepall_' + cid] = sweepall
|
||||
sweepall = True if have_data_entry(form_data, "sweepall_" + cid) else False
|
||||
page_data["wd_sweepall_" + cid] = sweepall
|
||||
value = None
|
||||
if not sweepall:
|
||||
try:
|
||||
value = form_data[bytes('amt_' + cid, 'utf-8')][0].decode('utf-8')
|
||||
page_data['wd_value_' + cid] = value
|
||||
except Exception as e:
|
||||
err_messages.append('Missing value')
|
||||
value = form_data[bytes("amt_" + cid, "utf-8")][0].decode("utf-8")
|
||||
page_data["wd_value_" + cid] = value
|
||||
except Exception as e: # noqa: F841
|
||||
err_messages.append("Missing value")
|
||||
try:
|
||||
address = form_data[bytes('to_' + cid, 'utf-8')][0].decode('utf-8')
|
||||
page_data['wd_address_' + cid] = address
|
||||
except Exception as e:
|
||||
err_messages.append('Missing address')
|
||||
address = form_data[bytes("to_" + cid, "utf-8")][0].decode("utf-8")
|
||||
page_data["wd_address_" + cid] = address
|
||||
except Exception as e: # noqa: F841
|
||||
err_messages.append("Missing address")
|
||||
|
||||
if estimate_fee and withdraw:
|
||||
err_messages.append('Estimate fee and withdraw can\'t be used together.')
|
||||
err_messages.append("Estimate fee and withdraw can't be used together.")
|
||||
if estimate_fee and coin_id not in (Coins.XMR, Coins.WOW):
|
||||
ci = swap_client.ci(coin_id)
|
||||
ticker: str = ci.ticker()
|
||||
err_messages.append(f'Estimate fee unavailable for {ticker}.')
|
||||
err_messages.append(f"Estimate fee unavailable for {ticker}.")
|
||||
|
||||
if coin_id == Coins.PART:
|
||||
try:
|
||||
type_from = form_data[bytes('withdraw_type_from_' + cid, 'utf-8')][0].decode('utf-8')
|
||||
type_to = form_data[bytes('withdraw_type_to_' + cid, 'utf-8')][0].decode('utf-8')
|
||||
page_data['wd_type_from_' + cid] = type_from
|
||||
page_data['wd_type_to_' + cid] = type_to
|
||||
except Exception as e:
|
||||
err_messages.append('Missing type')
|
||||
type_from = form_data[bytes("withdraw_type_from_" + cid, "utf-8")][
|
||||
0
|
||||
].decode("utf-8")
|
||||
type_to = form_data[bytes("withdraw_type_to_" + cid, "utf-8")][
|
||||
0
|
||||
].decode("utf-8")
|
||||
page_data["wd_type_from_" + cid] = type_from
|
||||
page_data["wd_type_to_" + cid] = type_to
|
||||
except Exception as e: # noqa: F841
|
||||
err_messages.append("Missing type")
|
||||
elif coin_id == Coins.LTC:
|
||||
try:
|
||||
type_from = form_data[bytes('withdraw_type_from_' + cid, 'utf-8')][0].decode('utf-8')
|
||||
page_data['wd_type_from_' + cid] = type_from
|
||||
except Exception as e:
|
||||
err_messages.append('Missing type')
|
||||
type_from = form_data[bytes("withdraw_type_from_" + cid, "utf-8")][
|
||||
0
|
||||
].decode("utf-8")
|
||||
page_data["wd_type_from_" + cid] = type_from
|
||||
except Exception as e: # noqa: F841
|
||||
err_messages.append("Missing type")
|
||||
|
||||
if len(err_messages) == 0:
|
||||
ci = swap_client.ci(coin_id)
|
||||
ticker: str = ci.ticker()
|
||||
try:
|
||||
if coin_id == Coins.PART:
|
||||
txid = swap_client.withdrawParticl(type_from, type_to, value, address, subfee)
|
||||
messages.append('Withdrew {} {} ({} to {}) to address {}<br/>In txid: {}'.format(value, ticker, type_from, type_to, address, txid))
|
||||
txid = swap_client.withdrawParticl(
|
||||
type_from, type_to, value, address, subfee
|
||||
)
|
||||
messages.append(
|
||||
"Withdrew {} {} ({} to {}) to address {}<br/>In txid: {}".format(
|
||||
value, ticker, type_from, type_to, address, txid
|
||||
)
|
||||
)
|
||||
elif coin_id == Coins.LTC:
|
||||
txid = swap_client.withdrawLTC(type_from, value, address, subfee)
|
||||
messages.append('Withdrew {} {} (from {}) to address {}<br/>In txid: {}'.format(value, ticker, type_from, address, txid))
|
||||
txid = swap_client.withdrawLTC(
|
||||
type_from, value, address, subfee
|
||||
)
|
||||
messages.append(
|
||||
"Withdrew {} {} (from {}) to address {}<br/>In txid: {}".format(
|
||||
value, ticker, type_from, address, txid
|
||||
)
|
||||
)
|
||||
elif coin_id in (Coins.XMR, Coins.WOW):
|
||||
if estimate_fee:
|
||||
fee_estimate = ci.estimateFee(value, address, sweepall)
|
||||
suffix = 's' if fee_estimate['num_txns'] > 1 else ''
|
||||
sum_fees = ci.format_amount(fee_estimate['sum_fee'])
|
||||
value_str = ci.format_amount(fee_estimate['sum_amount'])
|
||||
messages.append(f'Estimated fee for {value_str} {ticker} to address {address}: {sum_fees} in {fee_estimate["num_txns"]} transaction{suffix}.')
|
||||
page_data['fee_estimate'] = fee_estimate
|
||||
suffix = "s" if fee_estimate["num_txns"] > 1 else ""
|
||||
sum_fees = ci.format_amount(fee_estimate["sum_fee"])
|
||||
value_str = ci.format_amount(fee_estimate["sum_amount"])
|
||||
messages.append(
|
||||
f'Estimated fee for {value_str} {ticker} to address {address}: {sum_fees} in {fee_estimate["num_txns"]} transaction{suffix}.'
|
||||
)
|
||||
page_data["fee_estimate"] = fee_estimate
|
||||
else:
|
||||
txid = swap_client.withdrawCoin(coin_id, value, address, sweepall)
|
||||
txid = swap_client.withdrawCoin(
|
||||
coin_id, value, address, sweepall
|
||||
)
|
||||
if sweepall:
|
||||
messages.append('Swept all {} to address {}<br/>In txid: {}'.format(ticker, address, txid))
|
||||
messages.append(
|
||||
"Swept all {} to address {}<br/>In txid: {}".format(
|
||||
ticker, address, txid
|
||||
)
|
||||
)
|
||||
else:
|
||||
messages.append('Withdrew {} {} to address {}<br/>In txid: {}'.format(value, ticker, address, txid))
|
||||
messages.append('Note: The wallet balance can take a while to update.')
|
||||
messages.append(
|
||||
"Withdrew {} {} to address {}<br/>In txid: {}".format(
|
||||
value, ticker, address, txid
|
||||
)
|
||||
)
|
||||
messages.append(
|
||||
"Note: The wallet balance can take a while to update."
|
||||
)
|
||||
else:
|
||||
txid = swap_client.withdrawCoin(coin_id, value, address, subfee)
|
||||
messages.append('Withdrew {} {} to address {}<br/>In txid: {}'.format(value, ticker, address, txid))
|
||||
messages.append(
|
||||
"Withdrew {} {} to address {}<br/>In txid: {}".format(
|
||||
value, ticker, address, txid
|
||||
)
|
||||
)
|
||||
if not estimate_fee:
|
||||
withdrawal_successful = True
|
||||
except Exception as e:
|
||||
|
@ -232,41 +271,44 @@ def page_wallet(self, url_split, post_string):
|
|||
err_messages.append(str(e))
|
||||
if not estimate_fee:
|
||||
swap_client.updateWalletsInfo(True, only_coin=coin_id)
|
||||
elif have_data_entry(form_data, 'showutxogroups'):
|
||||
elif have_data_entry(form_data, "showutxogroups"):
|
||||
show_utxo_groups = True
|
||||
elif have_data_entry(form_data, 'create_utxo'):
|
||||
elif have_data_entry(form_data, "create_utxo"):
|
||||
show_utxo_groups = True
|
||||
try:
|
||||
value = get_data_entry(form_data, 'utxo_value')
|
||||
page_data['utxo_value'] = value
|
||||
value = get_data_entry(form_data, "utxo_value")
|
||||
page_data["utxo_value"] = value
|
||||
|
||||
ci = swap_client.ci(coin_id)
|
||||
value_sats = ci.make_int(value)
|
||||
|
||||
txid, address = ci.createUTXO(value_sats)
|
||||
messages.append('Created new utxo of value {} and address {}<br/>In txid: {}'.format(value, address, txid))
|
||||
messages.append(
|
||||
"Created new utxo of value {} and address {}<br/>In txid: {}".format(
|
||||
value, address, txid
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
err_messages.append(str(e))
|
||||
if swap_client.debug is True:
|
||||
swap_client.log.error(traceback.format_exc())
|
||||
|
||||
swap_client.updateWalletsInfo(force_refresh, only_coin=coin_id, wait_for_complete=True)
|
||||
wallets = swap_client.getCachedWalletsInfo({'coin_id': coin_id})
|
||||
swap_client.updateWalletsInfo(
|
||||
force_refresh, only_coin=coin_id, wait_for_complete=True
|
||||
)
|
||||
wallets = swap_client.getCachedWalletsInfo({"coin_id": coin_id})
|
||||
wallet_data = {}
|
||||
for k in wallets.keys():
|
||||
w = wallets[k]
|
||||
if 'error' in w:
|
||||
wallet_data = {
|
||||
'cid': str(int(k)),
|
||||
'error': w['error']
|
||||
}
|
||||
if "error" in w:
|
||||
wallet_data = {"cid": str(int(k)), "error": w["error"]}
|
||||
continue
|
||||
|
||||
if 'no_data' in w:
|
||||
if "no_data" in w:
|
||||
wallet_data = {
|
||||
'name': w['name'],
|
||||
'havedata': False,
|
||||
'updating': w['updating'],
|
||||
"name": w["name"],
|
||||
"havedata": False,
|
||||
"updating": w["updating"],
|
||||
}
|
||||
continue
|
||||
|
||||
|
@ -277,55 +319,68 @@ def page_wallet(self, url_split, post_string):
|
|||
|
||||
fee_rate, fee_src = swap_client.getFeeRateForCoin(k)
|
||||
est_fee = swap_client.estimateWithdrawFee(k, fee_rate)
|
||||
wallet_data['fee_rate'] = ci.format_amount(int(fee_rate * ci.COIN()))
|
||||
wallet_data['fee_rate_src'] = fee_src
|
||||
wallet_data['est_fee'] = 'Unknown' if est_fee is None else ci.format_amount(int(est_fee * ci.COIN()))
|
||||
wallet_data['deposit_address'] = w.get('deposit_address', 'Refresh necessary')
|
||||
wallet_data["fee_rate"] = ci.format_amount(int(fee_rate * ci.COIN()))
|
||||
wallet_data["fee_rate_src"] = fee_src
|
||||
wallet_data["est_fee"] = (
|
||||
"Unknown" if est_fee is None else ci.format_amount(int(est_fee * ci.COIN()))
|
||||
)
|
||||
wallet_data["deposit_address"] = w.get("deposit_address", "Refresh necessary")
|
||||
|
||||
if k in (Coins.XMR, Coins.WOW):
|
||||
wallet_data['main_address'] = w.get('main_address', 'Refresh necessary')
|
||||
wallet_data["main_address"] = w.get("main_address", "Refresh necessary")
|
||||
elif k == Coins.LTC:
|
||||
wallet_data['mweb_address'] = w.get('mweb_address', 'Refresh necessary')
|
||||
wallet_data["mweb_address"] = w.get("mweb_address", "Refresh necessary")
|
||||
|
||||
if 'wd_type_from_' + cid in page_data:
|
||||
wallet_data['wd_type_from'] = page_data['wd_type_from_' + cid]
|
||||
if 'wd_type_to_' + cid in page_data:
|
||||
wallet_data['wd_type_to'] = page_data['wd_type_to_' + cid]
|
||||
if "wd_type_from_" + cid in page_data:
|
||||
wallet_data["wd_type_from"] = page_data["wd_type_from_" + cid]
|
||||
if "wd_type_to_" + cid in page_data:
|
||||
wallet_data["wd_type_to"] = page_data["wd_type_to_" + cid]
|
||||
|
||||
if 'utxo_value' in page_data:
|
||||
wallet_data['utxo_value'] = page_data['utxo_value']
|
||||
if "utxo_value" in page_data:
|
||||
wallet_data["utxo_value"] = page_data["utxo_value"]
|
||||
|
||||
if not withdrawal_successful:
|
||||
if 'wd_value_' + cid in page_data:
|
||||
wallet_data['wd_value'] = page_data['wd_value_' + cid]
|
||||
if 'wd_address_' + cid in page_data:
|
||||
wallet_data['wd_address'] = page_data['wd_address_' + cid]
|
||||
if 'wd_subfee_' + cid in page_data:
|
||||
wallet_data['wd_subfee'] = page_data['wd_subfee_' + cid]
|
||||
if 'wd_sweepall_' + cid in page_data:
|
||||
wallet_data['wd_sweepall'] = page_data['wd_sweepall_' + cid]
|
||||
if 'fee_estimate' in page_data:
|
||||
wallet_data['est_fee'] = ci.format_amount(page_data['fee_estimate']['sum_fee'])
|
||||
wallet_data['fee_rate'] = ci.format_amount(page_data['fee_estimate']['sum_fee'] * 1000 // page_data['fee_estimate']['sum_weight'])
|
||||
if "wd_value_" + cid in page_data:
|
||||
wallet_data["wd_value"] = page_data["wd_value_" + cid]
|
||||
if "wd_address_" + cid in page_data:
|
||||
wallet_data["wd_address"] = page_data["wd_address_" + cid]
|
||||
if "wd_subfee_" + cid in page_data:
|
||||
wallet_data["wd_subfee"] = page_data["wd_subfee_" + cid]
|
||||
if "wd_sweepall_" + cid in page_data:
|
||||
wallet_data["wd_sweepall"] = page_data["wd_sweepall_" + cid]
|
||||
if "fee_estimate" in page_data:
|
||||
wallet_data["est_fee"] = ci.format_amount(
|
||||
page_data["fee_estimate"]["sum_fee"]
|
||||
)
|
||||
wallet_data["fee_rate"] = ci.format_amount(
|
||||
page_data["fee_estimate"]["sum_fee"]
|
||||
* 1000
|
||||
// page_data["fee_estimate"]["sum_weight"]
|
||||
)
|
||||
|
||||
if show_utxo_groups:
|
||||
utxo_groups = ''
|
||||
utxo_groups = ""
|
||||
unspent_by_addr = ci.getUnspentsByAddr()
|
||||
|
||||
sorted_unspent_by_addr = sorted(unspent_by_addr.items(), key=lambda x: x[1], reverse=True)
|
||||
sorted_unspent_by_addr = sorted(
|
||||
unspent_by_addr.items(), key=lambda x: x[1], reverse=True
|
||||
)
|
||||
for kv in sorted_unspent_by_addr:
|
||||
utxo_groups += kv[0] + ' ' + ci.format_amount(kv[1]) + '\n'
|
||||
utxo_groups += kv[0] + " " + ci.format_amount(kv[1]) + "\n"
|
||||
|
||||
wallet_data['show_utxo_groups'] = True
|
||||
wallet_data['utxo_groups'] = utxo_groups
|
||||
wallet_data["show_utxo_groups"] = True
|
||||
wallet_data["utxo_groups"] = utxo_groups
|
||||
|
||||
checkAddressesOwned(swap_client, ci, wallet_data)
|
||||
|
||||
template = server.env.get_template('wallet.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'w': wallet_data,
|
||||
'summary': summary,
|
||||
'block_unknown_seeds': swap_client._restrict_unknown_seed_wallets,
|
||||
})
|
||||
template = server.env.get_template("wallet.html")
|
||||
return self.render_template(
|
||||
template,
|
||||
{
|
||||
"messages": messages,
|
||||
"err_messages": err_messages,
|
||||
"w": wallet_data,
|
||||
"summary": summary,
|
||||
"block_unknown_seeds": swap_client._restrict_unknown_seed_wallets,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020-2024 tecnovert
|
||||
# Copyright (c) 2024 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -33,7 +34,21 @@ from basicswap.protocols.xmr_swap_1 import getChainBSplitKey, getChainBRemoteSpl
|
|||
|
||||
PAGE_LIMIT = 25
|
||||
invalid_coins_from = []
|
||||
known_chart_coins = ['BTC', 'PART', 'XMR', 'LTC', 'FIRO', 'DASH', 'PIVX', 'DOGE', 'ETH', 'DCR', 'ZANO', 'WOW', 'BCH']
|
||||
known_chart_coins = [
|
||||
"BTC",
|
||||
"PART",
|
||||
"XMR",
|
||||
"LTC",
|
||||
"FIRO",
|
||||
"DASH",
|
||||
"PIVX",
|
||||
"DOGE",
|
||||
"ETH",
|
||||
"DCR",
|
||||
"ZANO",
|
||||
"WOW",
|
||||
"BCH",
|
||||
]
|
||||
|
||||
|
||||
def tickerToCoinId(ticker):
|
||||
|
@ -41,7 +56,7 @@ def tickerToCoinId(ticker):
|
|||
for c in Coins:
|
||||
if c.name == search_str:
|
||||
return c.value
|
||||
raise ValueError('Unknown coin')
|
||||
raise ValueError("Unknown coin")
|
||||
|
||||
|
||||
def getCoinType(coin_type_ind):
|
||||
|
@ -55,9 +70,9 @@ def getCoinType(coin_type_ind):
|
|||
def validateAmountString(amount, ci):
|
||||
if not isinstance(amount, str):
|
||||
return
|
||||
ar = amount.split('.')
|
||||
ar = amount.split(".")
|
||||
if len(ar) > 1 and len(ar[1]) > ci.exp():
|
||||
raise ValueError('Too many decimal places in amount {}'.format(amount))
|
||||
raise ValueError("Too many decimal places in amount {}".format(amount))
|
||||
|
||||
|
||||
def inputAmount(amount_str, ci):
|
||||
|
@ -66,24 +81,24 @@ def inputAmount(amount_str, ci):
|
|||
|
||||
|
||||
def get_data_entry_or(post_data, name, default):
|
||||
if 'is_json' in post_data:
|
||||
if "is_json" in post_data:
|
||||
return post_data.get(name, default)
|
||||
key_bytes = name.encode('utf-8')
|
||||
key_bytes = name.encode("utf-8")
|
||||
if key_bytes in post_data:
|
||||
return post_data[key_bytes][0].decode('utf-8')
|
||||
return post_data[key_bytes][0].decode("utf-8")
|
||||
return default
|
||||
|
||||
|
||||
def get_data_entry(post_data, name):
|
||||
if 'is_json' in post_data:
|
||||
if "is_json" in post_data:
|
||||
return post_data[name]
|
||||
return post_data[name.encode('utf-8')][0].decode('utf-8')
|
||||
return post_data[name.encode("utf-8")][0].decode("utf-8")
|
||||
|
||||
|
||||
def have_data_entry(post_data, name):
|
||||
if 'is_json' in post_data:
|
||||
if "is_json" in post_data:
|
||||
return name in post_data
|
||||
return name.encode('utf-8') in post_data
|
||||
return name.encode("utf-8") in post_data
|
||||
|
||||
|
||||
def setCoinFilter(form_data, field_name):
|
||||
|
@ -96,18 +111,18 @@ def setCoinFilter(form_data, field_name):
|
|||
try:
|
||||
return Coins(coin_type)
|
||||
except Exception:
|
||||
raise ValueError('Unknown Coin Type {}'.format(str(field_name)))
|
||||
raise ValueError("Unknown Coin Type {}".format(str(field_name)))
|
||||
|
||||
|
||||
def set_pagination_filters(form_data, filters):
|
||||
if form_data and have_data_entry(form_data, 'pageback'):
|
||||
filters['page_no'] = int(form_data[b'pageno'][0]) - 1
|
||||
if filters['page_no'] < 1:
|
||||
filters['page_no'] = 1
|
||||
elif form_data and have_data_entry(form_data, 'pageforwards'):
|
||||
filters['page_no'] = int(form_data[b'pageno'][0]) + 1
|
||||
if filters['page_no'] > 1:
|
||||
filters['offset'] = (filters['page_no'] - 1) * PAGE_LIMIT
|
||||
if form_data and have_data_entry(form_data, "pageback"):
|
||||
filters["page_no"] = int(form_data[b"pageno"][0]) - 1
|
||||
if filters["page_no"] < 1:
|
||||
filters["page_no"] = 1
|
||||
elif form_data and have_data_entry(form_data, "pageforwards"):
|
||||
filters["page_no"] = int(form_data[b"pageno"][0]) + 1
|
||||
if filters["page_no"] > 1:
|
||||
filters["offset"] = (filters["page_no"] - 1) * PAGE_LIMIT
|
||||
|
||||
|
||||
def getTxIdHex(bid, tx_type, suffix):
|
||||
|
@ -116,12 +131,12 @@ def getTxIdHex(bid, tx_type, suffix):
|
|||
elif tx_type == TxTypes.PTX:
|
||||
obj = bid.participate_tx
|
||||
else:
|
||||
return 'Unknown Type'
|
||||
return "Unknown Type"
|
||||
|
||||
if not obj:
|
||||
return 'None'
|
||||
return "None"
|
||||
if not obj.txid:
|
||||
return 'None'
|
||||
return "None"
|
||||
return obj.txid.hex() + suffix
|
||||
|
||||
|
||||
|
@ -131,13 +146,13 @@ def getTxSpendHex(bid, tx_type):
|
|||
elif tx_type == TxTypes.PTX:
|
||||
obj = bid.participate_tx
|
||||
else:
|
||||
return 'Unknown Type'
|
||||
return "Unknown Type"
|
||||
|
||||
if not obj:
|
||||
return 'None'
|
||||
return "None"
|
||||
if not obj.spend_txid:
|
||||
return 'None'
|
||||
return obj.spend_txid.hex() + ' {}'.format(obj.spend_n)
|
||||
return "None"
|
||||
return obj.spend_txid.hex() + " {}".format(obj.spend_n)
|
||||
|
||||
|
||||
def listBidStates():
|
||||
|
@ -154,7 +169,19 @@ def listBidActions():
|
|||
return rv
|
||||
|
||||
|
||||
def describeBid(swap_client, bid, xmr_swap, offer, xmr_offer, bid_events, edit_bid, show_txns, view_tx_ind=None, for_api=False, show_lock_transfers=False):
|
||||
def describeBid(
|
||||
swap_client,
|
||||
bid,
|
||||
xmr_swap,
|
||||
offer,
|
||||
xmr_offer,
|
||||
bid_events,
|
||||
edit_bid,
|
||||
show_txns,
|
||||
view_tx_ind=None,
|
||||
for_api=False,
|
||||
show_lock_transfers=False,
|
||||
):
|
||||
ci_from = swap_client.ci(Coins(offer.coin_from))
|
||||
ci_to = swap_client.ci(Coins(offer.coin_to))
|
||||
|
||||
|
@ -166,240 +193,404 @@ def describeBid(swap_client, bid, xmr_swap, offer, xmr_offer, bid_events, edit_b
|
|||
bid_amount_to: int = bid.amount_to
|
||||
bid_rate: int = offer.rate if bid.rate is None else bid.rate
|
||||
|
||||
initiator_role: str = 'offerer' # Leader
|
||||
participant_role: str = 'bidder' # Follower
|
||||
initiator_role: str = "offerer" # Leader
|
||||
participant_role: str = "bidder" # Follower
|
||||
if reverse_bid:
|
||||
bid_amount = bid.amount_to
|
||||
bid_amount_to = bid.amount
|
||||
bid_rate = ci_from.make_int(bid.amount / bid.amount_to, r=1)
|
||||
initiator_role = 'bidder'
|
||||
participant_role = 'offerer'
|
||||
initiator_role = "bidder"
|
||||
participant_role = "offerer"
|
||||
|
||||
state_description = ''
|
||||
state_description = ""
|
||||
if offer.swap_type == SwapTypes.SELLER_FIRST:
|
||||
if bid.state == BidStates.BID_SENT:
|
||||
state_description = 'Waiting for seller to accept.'
|
||||
state_description = "Waiting for seller to accept."
|
||||
elif bid.state == BidStates.BID_RECEIVED:
|
||||
state_description = 'Waiting for seller to accept.'
|
||||
state_description = "Waiting for seller to accept."
|
||||
elif bid.state == BidStates.BID_ACCEPTED:
|
||||
if not bid.initiate_tx:
|
||||
state_description = 'Waiting for seller to send initiate tx.'
|
||||
state_description = "Waiting for seller to send initiate tx."
|
||||
else:
|
||||
state_description = 'Waiting for initiate tx to confirm.'
|
||||
state_description = "Waiting for initiate tx to confirm."
|
||||
elif bid.state == BidStates.SWAP_INITIATED:
|
||||
state_description = 'Waiting for participate txn to be confirmed in {} chain'.format(ci_follower.ticker())
|
||||
state_description = (
|
||||
"Waiting for participate txn to be confirmed in {} chain".format(
|
||||
ci_follower.ticker()
|
||||
)
|
||||
)
|
||||
elif bid.state == BidStates.SWAP_PARTICIPATING:
|
||||
if bid.was_sent:
|
||||
state_description = 'Waiting for participate txn to be spent in {} chain'.format(ci_follower.ticker())
|
||||
state_description = (
|
||||
"Waiting for participate txn to be spent in {} chain".format(
|
||||
ci_follower.ticker()
|
||||
)
|
||||
)
|
||||
else:
|
||||
state_description = 'Waiting for initiate txn to be spent in {} chain'.format(ci_leader.ticker())
|
||||
state_description = (
|
||||
"Waiting for initiate txn to be spent in {} chain".format(
|
||||
ci_leader.ticker()
|
||||
)
|
||||
)
|
||||
elif bid.state == BidStates.SWAP_COMPLETED:
|
||||
state_description = 'Swap completed'
|
||||
if bid.getITxState() == TxStates.TX_REDEEMED and bid.getPTxState() == TxStates.TX_REDEEMED:
|
||||
state_description += ' successfully'
|
||||
state_description = "Swap completed"
|
||||
if (
|
||||
bid.getITxState() == TxStates.TX_REDEEMED
|
||||
and bid.getPTxState() == TxStates.TX_REDEEMED
|
||||
):
|
||||
state_description += " successfully"
|
||||
else:
|
||||
state_description += ', ITX ' + strTxState(bid.getITxState()) + ', PTX ' + strTxState(bid.getPTxState())
|
||||
state_description += (
|
||||
", ITX "
|
||||
+ strTxState(bid.getITxState())
|
||||
+ ", PTX "
|
||||
+ strTxState(bid.getPTxState())
|
||||
)
|
||||
elif bid.state == BidStates.SWAP_TIMEDOUT:
|
||||
state_description = 'Timed out waiting for initiate txn'
|
||||
state_description = "Timed out waiting for initiate txn"
|
||||
elif bid.state == BidStates.BID_ABANDONED:
|
||||
state_description = 'Bid abandoned'
|
||||
state_description = "Bid abandoned"
|
||||
elif bid.state == BidStates.BID_ERROR:
|
||||
state_description = bid.state_note
|
||||
elif offer.swap_type == SwapTypes.XMR_SWAP:
|
||||
if bid.state == BidStates.BID_SENT:
|
||||
state_description = 'Waiting for offerer to accept'
|
||||
state_description = "Waiting for offerer to accept"
|
||||
if bid.state == BidStates.BID_RECEIVING:
|
||||
# Offerer receiving bid from bidder
|
||||
state_description = 'Waiting for bid to be fully received'
|
||||
state_description = "Waiting for bid to be fully received"
|
||||
elif bid.state == BidStates.BID_RECEIVED:
|
||||
# Offerer received bid from bidder
|
||||
# TODO: Manual vs automatic
|
||||
state_description = 'Bid must be accepted'
|
||||
state_description = "Bid must be accepted"
|
||||
elif bid.state == BidStates.BID_RECEIVING_ACC:
|
||||
state_description = 'Receiving accepted bid message'
|
||||
state_description = "Receiving accepted bid message"
|
||||
elif bid.state == BidStates.BID_ACCEPTED:
|
||||
state_description = 'Offerer has accepted bid, waiting for bidder to respond'
|
||||
state_description = (
|
||||
"Offerer has accepted bid, waiting for bidder to respond"
|
||||
)
|
||||
elif bid.state == BidStates.SWAP_DELAYING:
|
||||
last_state = getLastBidState(bid.states)
|
||||
if last_state == BidStates.BID_RECEIVED:
|
||||
state_description = 'Delaying before accepting bid'
|
||||
state_description = "Delaying before accepting bid"
|
||||
elif last_state == BidStates.BID_RECEIVING_ACC:
|
||||
state_description = 'Delaying before responding to accepted bid'
|
||||
state_description = "Delaying before responding to accepted bid"
|
||||
elif last_state == BidStates.XMR_SWAP_SCRIPT_TX_REDEEMED:
|
||||
state_description = f'Delaying before spending from {ci_follower.ticker()} lock tx'
|
||||
state_description = (
|
||||
f"Delaying before spending from {ci_follower.ticker()} lock tx"
|
||||
)
|
||||
elif last_state == BidStates.BID_ACCEPTED:
|
||||
state_description = f'Delaying before sending {ci_leader.ticker()} lock tx'
|
||||
state_description = (
|
||||
f"Delaying before sending {ci_leader.ticker()} lock tx"
|
||||
)
|
||||
else:
|
||||
state_description = 'Delaying before automated action'
|
||||
state_description = "Delaying before automated action"
|
||||
elif bid.state == BidStates.XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX:
|
||||
state_description = f'Waiting for {ci_leader.ticker()} lock tx to confirm in chain ({ci_leader.blocks_confirmed} blocks)'
|
||||
state_description = f"Waiting for {ci_leader.ticker()} lock tx to confirm in chain ({ci_leader.blocks_confirmed} blocks)"
|
||||
elif bid.state == BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED:
|
||||
if xmr_swap.b_lock_tx_id is None:
|
||||
state_description = f'Waiting for {ci_follower.ticker()} lock tx'
|
||||
state_description = f"Waiting for {ci_follower.ticker()} lock tx"
|
||||
else:
|
||||
state_description = f'Waiting for {ci_follower.ticker()} lock tx to confirm in chain ({ci_follower.blocks_confirmed} blocks)'
|
||||
state_description = f"Waiting for {ci_follower.ticker()} lock tx to confirm in chain ({ci_follower.blocks_confirmed} blocks)"
|
||||
elif bid.state == BidStates.XMR_SWAP_NOSCRIPT_COIN_LOCKED:
|
||||
state_description = f'Waiting for {initiator_role} to unlock {ci_leader.ticker()} lock tx'
|
||||
state_description = (
|
||||
f"Waiting for {initiator_role} to unlock {ci_leader.ticker()} lock tx"
|
||||
)
|
||||
elif bid.state == BidStates.XMR_SWAP_LOCK_RELEASED:
|
||||
state_description = f'Waiting for {participant_role} to spend from {ci_leader.ticker()} lock tx'
|
||||
state_description = f"Waiting for {participant_role} to spend from {ci_leader.ticker()} lock tx"
|
||||
elif bid.state == BidStates.XMR_SWAP_SCRIPT_TX_REDEEMED:
|
||||
state_description = f'Waiting for {initiator_role} to spend from {ci_follower.ticker()} lock tx'
|
||||
state_description = f"Waiting for {initiator_role} to spend from {ci_follower.ticker()} lock tx"
|
||||
elif bid.state == BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED:
|
||||
state_description = f'Waiting for {ci_follower.ticker()} lock tx spend tx to confirm in chain'
|
||||
state_description = f"Waiting for {ci_follower.ticker()} lock tx spend tx to confirm in chain"
|
||||
elif bid.state == BidStates.XMR_SWAP_SCRIPT_TX_PREREFUND:
|
||||
if bid.was_sent:
|
||||
state_description = f'Waiting for {initiator_role} to redeem or locktime to expire'
|
||||
state_description = (
|
||||
f"Waiting for {initiator_role} to redeem or locktime to expire"
|
||||
)
|
||||
else:
|
||||
state_description = 'Redeeming output'
|
||||
state_description = "Redeeming output"
|
||||
|
||||
addr_label = swap_client.getAddressLabel([bid.bid_addr, ])[0]
|
||||
addr_label = swap_client.getAddressLabel(
|
||||
[
|
||||
bid.bid_addr,
|
||||
]
|
||||
)[0]
|
||||
|
||||
can_abandon: bool = False
|
||||
if swap_client.debug and bid.state not in (BidStates.BID_ABANDONED, BidStates.SWAP_COMPLETED):
|
||||
if swap_client.debug and bid.state not in (
|
||||
BidStates.BID_ABANDONED,
|
||||
BidStates.SWAP_COMPLETED,
|
||||
):
|
||||
can_abandon = True
|
||||
|
||||
data = {
|
||||
'coin_from': ci_from.coin_name(),
|
||||
'coin_to': ci_to.coin_name(),
|
||||
'amt_from': ci_from.format_amount(bid_amount),
|
||||
'amt_to': ci_to.format_amount(bid_amount_to),
|
||||
'bid_rate': ci_to.format_amount(bid_rate),
|
||||
'ticker_from': ci_from.ticker(),
|
||||
'ticker_to': ci_to.ticker(),
|
||||
'bid_state': strBidState(bid.state),
|
||||
'state_description': state_description,
|
||||
'itx_state': strTxState(bid.getITxState()),
|
||||
'ptx_state': strTxState(bid.getPTxState()),
|
||||
'offer_id': bid.offer_id.hex(),
|
||||
'addr_from': bid.bid_addr,
|
||||
'addr_from_label': addr_label,
|
||||
'addr_fund_proof': bid.proof_address,
|
||||
'created_at': bid.created_at if for_api else format_timestamp(bid.created_at, with_seconds=True),
|
||||
'expired_at': bid.expire_at if for_api else format_timestamp(bid.expire_at, with_seconds=True),
|
||||
'was_sent': 'True' if bid.was_sent else 'False',
|
||||
'was_received': 'True' if bid.was_received else 'False',
|
||||
'initiate_tx': getTxIdHex(bid, TxTypes.ITX, ' ' + ci_leader.ticker()),
|
||||
'initiate_conf': 'None' if (not bid.initiate_tx or not bid.initiate_tx.conf) else bid.initiate_tx.conf,
|
||||
'participate_tx': getTxIdHex(bid, TxTypes.PTX, ' ' + ci_follower.ticker()),
|
||||
'participate_conf': 'None' if (not bid.participate_tx or not bid.participate_tx.conf) else bid.participate_tx.conf,
|
||||
'show_txns': show_txns,
|
||||
'can_abandon': can_abandon,
|
||||
'events': bid_events,
|
||||
'debug_ui': swap_client.debug_ui,
|
||||
'reverse_bid': reverse_bid,
|
||||
"coin_from": ci_from.coin_name(),
|
||||
"coin_to": ci_to.coin_name(),
|
||||
"amt_from": ci_from.format_amount(bid_amount),
|
||||
"amt_to": ci_to.format_amount(bid_amount_to),
|
||||
"bid_rate": ci_to.format_amount(bid_rate),
|
||||
"ticker_from": ci_from.ticker(),
|
||||
"ticker_to": ci_to.ticker(),
|
||||
"bid_state": strBidState(bid.state),
|
||||
"state_description": state_description,
|
||||
"itx_state": strTxState(bid.getITxState()),
|
||||
"ptx_state": strTxState(bid.getPTxState()),
|
||||
"offer_id": bid.offer_id.hex(),
|
||||
"addr_from": bid.bid_addr,
|
||||
"addr_from_label": addr_label,
|
||||
"addr_fund_proof": bid.proof_address,
|
||||
"created_at": (
|
||||
bid.created_at
|
||||
if for_api
|
||||
else format_timestamp(bid.created_at, with_seconds=True)
|
||||
),
|
||||
"expired_at": (
|
||||
bid.expire_at
|
||||
if for_api
|
||||
else format_timestamp(bid.expire_at, with_seconds=True)
|
||||
),
|
||||
"was_sent": "True" if bid.was_sent else "False",
|
||||
"was_received": "True" if bid.was_received else "False",
|
||||
"initiate_tx": getTxIdHex(bid, TxTypes.ITX, " " + ci_leader.ticker()),
|
||||
"initiate_conf": (
|
||||
"None"
|
||||
if (not bid.initiate_tx or not bid.initiate_tx.conf)
|
||||
else bid.initiate_tx.conf
|
||||
),
|
||||
"participate_tx": getTxIdHex(bid, TxTypes.PTX, " " + ci_follower.ticker()),
|
||||
"participate_conf": (
|
||||
"None"
|
||||
if (not bid.participate_tx or not bid.participate_tx.conf)
|
||||
else bid.participate_tx.conf
|
||||
),
|
||||
"show_txns": show_txns,
|
||||
"can_abandon": can_abandon,
|
||||
"events": bid_events,
|
||||
"debug_ui": swap_client.debug_ui,
|
||||
"reverse_bid": reverse_bid,
|
||||
}
|
||||
|
||||
if edit_bid:
|
||||
data['bid_state_ind'] = int(bid.state)
|
||||
data['bid_states'] = listBidStates()
|
||||
data["bid_state_ind"] = int(bid.state)
|
||||
data["bid_states"] = listBidStates()
|
||||
|
||||
if swap_client.debug_ui:
|
||||
data['debug_ind'] = bid.debug_ind
|
||||
data['debug_options'] = [(int(t), t.name) for t in DebugTypes]
|
||||
data["debug_ind"] = bid.debug_ind
|
||||
data["debug_options"] = [(int(t), t.name) for t in DebugTypes]
|
||||
|
||||
if show_txns:
|
||||
if offer.swap_type == SwapTypes.XMR_SWAP:
|
||||
txns = []
|
||||
if bid.xmr_a_lock_tx:
|
||||
confirms = None
|
||||
if swap_client.coin_clients[ci_leader.coin_type()]['chain_height'] and bid.xmr_a_lock_tx.chain_height:
|
||||
confirms = (swap_client.coin_clients[ci_leader.coin_type()]['chain_height'] - bid.xmr_a_lock_tx.chain_height) + 1
|
||||
txns.append({'type': 'Chain A Lock', 'txid': hex_or_none(bid.xmr_a_lock_tx.txid), 'confirms': confirms})
|
||||
if (
|
||||
swap_client.coin_clients[ci_leader.coin_type()]["chain_height"]
|
||||
and bid.xmr_a_lock_tx.chain_height
|
||||
):
|
||||
confirms = (
|
||||
swap_client.coin_clients[ci_leader.coin_type()]["chain_height"]
|
||||
- bid.xmr_a_lock_tx.chain_height
|
||||
) + 1
|
||||
txns.append(
|
||||
{
|
||||
"type": "Chain A Lock",
|
||||
"txid": hex_or_none(bid.xmr_a_lock_tx.txid),
|
||||
"confirms": confirms,
|
||||
}
|
||||
)
|
||||
if bid.xmr_a_lock_spend_tx:
|
||||
txns.append({'type': 'Chain A Lock Spend', 'txid': bid.xmr_a_lock_spend_tx.txid.hex()})
|
||||
txns.append(
|
||||
{
|
||||
"type": "Chain A Lock Spend",
|
||||
"txid": bid.xmr_a_lock_spend_tx.txid.hex(),
|
||||
}
|
||||
)
|
||||
if bid.xmr_b_lock_tx:
|
||||
confirms = None
|
||||
if swap_client.coin_clients[ci_follower.coin_type()]['chain_height'] and bid.xmr_b_lock_tx.chain_height:
|
||||
confirms = (swap_client.coin_clients[ci_follower.coin_type()]['chain_height'] - bid.xmr_b_lock_tx.chain_height) + 1
|
||||
txns.append({'type': 'Chain B Lock', 'txid': bid.xmr_b_lock_tx.txid.hex(), 'confirms': confirms})
|
||||
if (
|
||||
swap_client.coin_clients[ci_follower.coin_type()]["chain_height"]
|
||||
and bid.xmr_b_lock_tx.chain_height
|
||||
):
|
||||
confirms = (
|
||||
swap_client.coin_clients[ci_follower.coin_type()][
|
||||
"chain_height"
|
||||
]
|
||||
- bid.xmr_b_lock_tx.chain_height
|
||||
) + 1
|
||||
txns.append(
|
||||
{
|
||||
"type": "Chain B Lock",
|
||||
"txid": bid.xmr_b_lock_tx.txid.hex(),
|
||||
"confirms": confirms,
|
||||
}
|
||||
)
|
||||
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()})
|
||||
txns.append(
|
||||
{
|
||||
"type": "Chain B Lock Spend",
|
||||
"txid": bid.xmr_b_lock_tx.spend_txid.hex(),
|
||||
}
|
||||
)
|
||||
if xmr_swap.a_lock_refund_tx:
|
||||
txns.append({'type': strTxType(TxTypes.XMR_SWAP_A_LOCK_REFUND), 'txid': xmr_swap.a_lock_refund_tx_id.hex()})
|
||||
txns.append(
|
||||
{
|
||||
"type": strTxType(TxTypes.XMR_SWAP_A_LOCK_REFUND),
|
||||
"txid": xmr_swap.a_lock_refund_tx_id.hex(),
|
||||
}
|
||||
)
|
||||
if xmr_swap.a_lock_refund_spend_tx:
|
||||
txns.append({'type': strTxType(TxTypes.XMR_SWAP_A_LOCK_REFUND_SPEND), 'txid': xmr_swap.a_lock_refund_spend_tx_id.hex()})
|
||||
txns.append(
|
||||
{
|
||||
"type": strTxType(TxTypes.XMR_SWAP_A_LOCK_REFUND_SPEND),
|
||||
"txid": xmr_swap.a_lock_refund_spend_tx_id.hex(),
|
||||
}
|
||||
)
|
||||
for tx_type, tx in bid.txns.items():
|
||||
if tx_type in (TxTypes.XMR_SWAP_A_LOCK_REFUND, TxTypes.XMR_SWAP_A_LOCK_REFUND_SPEND):
|
||||
if tx_type in (
|
||||
TxTypes.XMR_SWAP_A_LOCK_REFUND,
|
||||
TxTypes.XMR_SWAP_A_LOCK_REFUND_SPEND,
|
||||
):
|
||||
continue
|
||||
txns.append({'type': strTxType(tx_type), 'txid': tx.txid.hex()})
|
||||
data['txns'] = txns
|
||||
txns.append({"type": strTxType(tx_type), "txid": tx.txid.hex()})
|
||||
data["txns"] = txns
|
||||
|
||||
data['xmr_b_shared_address'] = ci_to.encodeSharedAddress(xmr_swap.pkbv, xmr_swap.pkbs) if xmr_swap.pkbs else None
|
||||
data['xmr_b_shared_viewkey'] = ci_to.encodeKey(xmr_swap.vkbv) if xmr_swap.vkbv else None
|
||||
data["xmr_b_shared_address"] = (
|
||||
ci_to.encodeSharedAddress(xmr_swap.pkbv, xmr_swap.pkbs)
|
||||
if xmr_swap.pkbs
|
||||
else None
|
||||
)
|
||||
data["xmr_b_shared_viewkey"] = (
|
||||
ci_to.encodeKey(xmr_swap.vkbv) if xmr_swap.vkbv else None
|
||||
)
|
||||
|
||||
if swap_client.debug_ui:
|
||||
try:
|
||||
data['xmr_b_half_privatekey'] = getChainBSplitKey(swap_client, bid, xmr_swap, offer)
|
||||
except Exception as e:
|
||||
swap_client.log.debug('Unable to get xmr_b_half_privatekey for bid: {}'.format(bid.bid_id.hex()))
|
||||
data["xmr_b_half_privatekey"] = getChainBSplitKey(
|
||||
swap_client, bid, xmr_swap, offer
|
||||
)
|
||||
except Exception as e: # noqa: F841
|
||||
swap_client.log.debug(
|
||||
"Unable to get xmr_b_half_privatekey for bid: {}".format(
|
||||
bid.bid_id.hex()
|
||||
)
|
||||
)
|
||||
try:
|
||||
remote_split_key = getChainBRemoteSplitKey(swap_client, bid, xmr_swap, offer)
|
||||
remote_split_key = getChainBRemoteSplitKey(
|
||||
swap_client, bid, xmr_swap, offer
|
||||
)
|
||||
if remote_split_key:
|
||||
data['xmr_b_half_privatekey_remote'] = remote_split_key
|
||||
except Exception as e:
|
||||
swap_client.log.debug('Unable to get xmr_b_half_privatekey_remote for bid: {}'.format(bid.bid_id.hex()))
|
||||
data["xmr_b_half_privatekey_remote"] = remote_split_key
|
||||
except Exception as e: # noqa: F841
|
||||
swap_client.log.debug(
|
||||
"Unable to get xmr_b_half_privatekey_remote for bid: {}".format(
|
||||
bid.bid_id.hex()
|
||||
)
|
||||
)
|
||||
|
||||
if show_lock_transfers:
|
||||
if xmr_swap.pkbs:
|
||||
data['lock_transfers'] = json.dumps(ci_to.showLockTransfers(xmr_swap.vkbv, xmr_swap.pkbs, bid.chain_b_height_start), indent=4)
|
||||
data["lock_transfers"] = json.dumps(
|
||||
ci_to.showLockTransfers(
|
||||
xmr_swap.vkbv, xmr_swap.pkbs, bid.chain_b_height_start
|
||||
),
|
||||
indent=4,
|
||||
)
|
||||
else:
|
||||
data['lock_transfers'] = 'Shared address not yet known.'
|
||||
data["lock_transfers"] = "Shared address not yet known."
|
||||
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)
|
||||
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)
|
||||
|
||||
if bid.initiate_tx and bid.initiate_tx.tx_data is not None:
|
||||
data['initiate_tx_inputs'] = ci_from.listInputs(bid.initiate_tx.tx_data)
|
||||
data["initiate_tx_inputs"] = ci_from.listInputs(bid.initiate_tx.tx_data)
|
||||
if bid.participate_tx and bid.participate_tx.tx_data is not None:
|
||||
data['initiate_tx_inputs'] = ci_from.listInputs(bid.participate_tx.tx_data)
|
||||
data["initiate_tx_inputs"] = ci_from.listInputs(
|
||||
bid.participate_tx.tx_data
|
||||
)
|
||||
|
||||
if offer.swap_type == SwapTypes.XMR_SWAP:
|
||||
data['coin_a_lock_refund_tx_est_final'] = 'None'
|
||||
data['coin_a_lock_refund_swipe_tx_est_final'] = 'None'
|
||||
data["coin_a_lock_refund_tx_est_final"] = "None"
|
||||
data["coin_a_lock_refund_swipe_tx_est_final"] = "None"
|
||||
|
||||
if offer.lock_type == TxLockTypes.SEQUENCE_LOCK_TIME:
|
||||
if bid.xmr_a_lock_tx and bid.xmr_a_lock_tx.block_time:
|
||||
raw_sequence = ci_leader.getExpectedSequence(offer.lock_type, offer.lock_value)
|
||||
raw_sequence = ci_leader.getExpectedSequence(
|
||||
offer.lock_type, offer.lock_value
|
||||
)
|
||||
seconds_locked = ci_leader.decodeSequence(raw_sequence)
|
||||
data['coin_a_lock_refund_tx_est_final'] = bid.xmr_a_lock_tx.block_time + seconds_locked
|
||||
data['coin_a_last_median_time'] = swap_client.coin_clients[offer.coin_from]['chain_median_time']
|
||||
data["coin_a_lock_refund_tx_est_final"] = (
|
||||
bid.xmr_a_lock_tx.block_time + seconds_locked
|
||||
)
|
||||
data["coin_a_last_median_time"] = swap_client.coin_clients[
|
||||
offer.coin_from
|
||||
]["chain_median_time"]
|
||||
|
||||
if TxTypes.XMR_SWAP_A_LOCK_REFUND in bid.txns:
|
||||
refund_tx = bid.txns[TxTypes.XMR_SWAP_A_LOCK_REFUND]
|
||||
if refund_tx.block_time is not None:
|
||||
raw_sequence = ci_leader.getExpectedSequence(offer.lock_type, offer.lock_value)
|
||||
raw_sequence = ci_leader.getExpectedSequence(
|
||||
offer.lock_type, offer.lock_value
|
||||
)
|
||||
seconds_locked = ci_leader.decodeSequence(raw_sequence)
|
||||
data['coin_a_lock_refund_swipe_tx_est_final'] = refund_tx.block_time + seconds_locked
|
||||
data["coin_a_lock_refund_swipe_tx_est_final"] = (
|
||||
refund_tx.block_time + seconds_locked
|
||||
)
|
||||
|
||||
if view_tx_ind:
|
||||
data['view_tx_ind'] = view_tx_ind
|
||||
data["view_tx_ind"] = view_tx_ind
|
||||
view_tx_id = bytes.fromhex(view_tx_ind)
|
||||
|
||||
if xmr_swap:
|
||||
if view_tx_id == xmr_swap.a_lock_tx_id and xmr_swap.a_lock_tx:
|
||||
data['view_tx_hex'] = xmr_swap.a_lock_tx.hex()
|
||||
data['chain_a_lock_tx_inputs'] = ci_leader.listInputs(xmr_swap.a_lock_tx)
|
||||
if view_tx_id == xmr_swap.a_lock_refund_tx_id and xmr_swap.a_lock_refund_tx:
|
||||
data['view_tx_hex'] = xmr_swap.a_lock_refund_tx.hex()
|
||||
if view_tx_id == xmr_swap.a_lock_refund_spend_tx_id and xmr_swap.a_lock_refund_spend_tx:
|
||||
data['view_tx_hex'] = xmr_swap.a_lock_refund_spend_tx.hex()
|
||||
if view_tx_id == xmr_swap.a_lock_spend_tx_id and xmr_swap.a_lock_spend_tx:
|
||||
data['view_tx_hex'] = xmr_swap.a_lock_spend_tx.hex()
|
||||
data["view_tx_hex"] = xmr_swap.a_lock_tx.hex()
|
||||
data["chain_a_lock_tx_inputs"] = ci_leader.listInputs(
|
||||
xmr_swap.a_lock_tx
|
||||
)
|
||||
if (
|
||||
view_tx_id == xmr_swap.a_lock_refund_tx_id
|
||||
and xmr_swap.a_lock_refund_tx
|
||||
):
|
||||
data["view_tx_hex"] = xmr_swap.a_lock_refund_tx.hex()
|
||||
if (
|
||||
view_tx_id == xmr_swap.a_lock_refund_spend_tx_id
|
||||
and xmr_swap.a_lock_refund_spend_tx
|
||||
):
|
||||
data["view_tx_hex"] = xmr_swap.a_lock_refund_spend_tx.hex()
|
||||
if (
|
||||
view_tx_id == xmr_swap.a_lock_spend_tx_id
|
||||
and xmr_swap.a_lock_spend_tx
|
||||
):
|
||||
data["view_tx_hex"] = xmr_swap.a_lock_spend_tx.hex()
|
||||
|
||||
if 'view_tx_hex' in data:
|
||||
data['view_tx_desc'] = json.dumps(ci_leader.describeTx(data['view_tx_hex']), indent=4)
|
||||
if "view_tx_hex" in data:
|
||||
data["view_tx_desc"] = json.dumps(
|
||||
ci_leader.describeTx(data["view_tx_hex"]), indent=4
|
||||
)
|
||||
else:
|
||||
if offer.lock_type == TxLockTypes.SEQUENCE_LOCK_TIME:
|
||||
if bid.initiate_tx and bid.initiate_tx.block_time is not None:
|
||||
raw_sequence = ci_leader.getExpectedSequence(offer.lock_type, offer.lock_value)
|
||||
raw_sequence = ci_leader.getExpectedSequence(
|
||||
offer.lock_type, offer.lock_value
|
||||
)
|
||||
seconds_locked = ci_leader.decodeSequence(raw_sequence)
|
||||
data['itx_refund_tx_est_final'] = bid.initiate_tx.block_time + seconds_locked
|
||||
data["itx_refund_tx_est_final"] = (
|
||||
bid.initiate_tx.block_time + seconds_locked
|
||||
)
|
||||
if bid.participate_tx and bid.participate_tx.block_time is not None:
|
||||
raw_sequence = ci_follower.getExpectedSequence(offer.lock_type, offer.lock_value // 2)
|
||||
raw_sequence = ci_follower.getExpectedSequence(
|
||||
offer.lock_type, offer.lock_value // 2
|
||||
)
|
||||
seconds_locked = ci_follower.decodeSequence(raw_sequence)
|
||||
data['ptx_refund_tx_est_final'] = bid.participate_tx.block_time + seconds_locked
|
||||
data["ptx_refund_tx_est_final"] = (
|
||||
bid.participate_tx.block_time + seconds_locked
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
|
@ -408,20 +599,24 @@ def listOldBidStates(bid):
|
|||
old_states = []
|
||||
num_states = len(bid.states) // 12
|
||||
for i in range(num_states):
|
||||
up = struct.unpack_from('<iq', bid.states[i * 12:(i + 1) * 12])
|
||||
old_states.append((up[1], 'Bid ' + strBidState(up[0])))
|
||||
up = struct.unpack_from("<iq", bid.states[i * 12 : (i + 1) * 12])
|
||||
old_states.append((up[1], "Bid " + strBidState(up[0])))
|
||||
if bid.initiate_tx and bid.initiate_tx.states is not None:
|
||||
num_states = len(bid.initiate_tx.states) // 12
|
||||
for i in range(num_states):
|
||||
up = struct.unpack_from('<iq', bid.initiate_tx.states[i * 12:(i + 1) * 12])
|
||||
up = struct.unpack_from(
|
||||
"<iq", bid.initiate_tx.states[i * 12 : (i + 1) * 12]
|
||||
)
|
||||
if up[0] != TxStates.TX_NONE:
|
||||
old_states.append((up[1], 'ITX ' + strTxState(up[0])))
|
||||
old_states.append((up[1], "ITX " + strTxState(up[0])))
|
||||
if bid.participate_tx and bid.participate_tx.states is not None:
|
||||
num_states = len(bid.participate_tx.states) // 12
|
||||
for i in range(num_states):
|
||||
up = struct.unpack_from('<iq', bid.participate_tx.states[i * 12:(i + 1) * 12])
|
||||
up = struct.unpack_from(
|
||||
"<iq", bid.participate_tx.states[i * 12 : (i + 1) * 12]
|
||||
)
|
||||
if up[0] != TxStates.TX_NONE:
|
||||
old_states.append((up[1], 'PTX ' + strTxState(up[0])))
|
||||
old_states.append((up[1], "PTX " + strTxState(up[0])))
|
||||
if len(old_states) > 0:
|
||||
old_states.sort(key=lambda x: x[0])
|
||||
return old_states
|
||||
|
@ -429,16 +624,16 @@ def listOldBidStates(bid):
|
|||
|
||||
def getCoinName(c):
|
||||
if c == Coins.PART_ANON:
|
||||
return chainparams[Coins.PART]['name'].capitalize() + ' Anon'
|
||||
return chainparams[Coins.PART]["name"].capitalize() + " Anon"
|
||||
if c == Coins.PART_BLIND:
|
||||
return chainparams[Coins.PART]['name'].capitalize() + ' Blind'
|
||||
return chainparams[Coins.PART]["name"].capitalize() + " Blind"
|
||||
if c == Coins.LTC_MWEB:
|
||||
return chainparams[Coins.LTC]['name'].capitalize() + ' MWEB'
|
||||
return chainparams[Coins.LTC]["name"].capitalize() + " MWEB"
|
||||
|
||||
coin_chainparams = chainparams[c]
|
||||
if 'display_name' in coin_chainparams:
|
||||
return coin_chainparams['display_name']
|
||||
return coin_chainparams['name'].capitalize()
|
||||
if "display_name" in coin_chainparams:
|
||||
return coin_chainparams["display_name"]
|
||||
return coin_chainparams["name"].capitalize()
|
||||
|
||||
|
||||
def listAvailableCoins(swap_client, with_variants=True, split_from=False):
|
||||
|
@ -447,7 +642,7 @@ def listAvailableCoins(swap_client, with_variants=True, split_from=False):
|
|||
for k, v in swap_client.coin_clients.items():
|
||||
if k not in chainparams:
|
||||
continue
|
||||
if v['connection_type'] == 'rpc':
|
||||
if v["connection_type"] == "rpc":
|
||||
coins.append((int(k), getCoinName(k)))
|
||||
if split_from and k not in invalid_coins_from:
|
||||
coins_from.append(coins[-1])
|
||||
|
@ -457,7 +652,7 @@ def listAvailableCoins(swap_client, with_variants=True, split_from=False):
|
|||
if split_from and v not in invalid_coins_from:
|
||||
coins_from.append(coins[-1])
|
||||
if with_variants and k == Coins.LTC:
|
||||
for v in (Coins.LTC_MWEB, ):
|
||||
for v in (Coins.LTC_MWEB,):
|
||||
pass # Add when swappable
|
||||
if split_from:
|
||||
return coins_from, coins
|
||||
|
@ -465,29 +660,37 @@ def listAvailableCoins(swap_client, with_variants=True, split_from=False):
|
|||
|
||||
|
||||
def checkAddressesOwned(swap_client, ci, wallet_info):
|
||||
if 'stealth_address' in wallet_info:
|
||||
if "stealth_address" in wallet_info:
|
||||
|
||||
if wallet_info['stealth_address'] != '?':
|
||||
if not ci.isAddressMine(wallet_info['stealth_address']):
|
||||
ci._log.error('Unowned stealth address: {}'.format(wallet_info['stealth_address']))
|
||||
wallet_info['stealth_address'] = 'Error: unowned address'
|
||||
elif swap_client._restrict_unknown_seed_wallets and not ci.knownWalletSeed():
|
||||
wallet_info['stealth_address'] = 'WARNING: Unknown wallet seed'
|
||||
if wallet_info["stealth_address"] != "?":
|
||||
if not ci.isAddressMine(wallet_info["stealth_address"]):
|
||||
ci._log.error(
|
||||
"Unowned stealth address: {}".format(wallet_info["stealth_address"])
|
||||
)
|
||||
wallet_info["stealth_address"] = "Error: unowned address"
|
||||
elif (
|
||||
swap_client._restrict_unknown_seed_wallets and not ci.knownWalletSeed()
|
||||
):
|
||||
wallet_info["stealth_address"] = "WARNING: Unknown wallet seed"
|
||||
|
||||
if 'deposit_address' in wallet_info:
|
||||
if wallet_info['deposit_address'] != 'Refresh necessary':
|
||||
if not ci.isAddressMine(wallet_info['deposit_address']):
|
||||
ci._log.error('Unowned deposit address: {}'.format(wallet_info['deposit_address']))
|
||||
wallet_info['deposit_address'] = 'Error: unowned address'
|
||||
elif swap_client._restrict_unknown_seed_wallets and not ci.knownWalletSeed():
|
||||
wallet_info['deposit_address'] = 'WARNING: Unknown wallet seed'
|
||||
if "deposit_address" in wallet_info:
|
||||
if wallet_info["deposit_address"] != "Refresh necessary":
|
||||
if not ci.isAddressMine(wallet_info["deposit_address"]):
|
||||
ci._log.error(
|
||||
"Unowned deposit address: {}".format(wallet_info["deposit_address"])
|
||||
)
|
||||
wallet_info["deposit_address"] = "Error: unowned address"
|
||||
elif (
|
||||
swap_client._restrict_unknown_seed_wallets and not ci.knownWalletSeed()
|
||||
):
|
||||
wallet_info["deposit_address"] = "WARNING: Unknown wallet seed"
|
||||
|
||||
|
||||
def validateTextInput(text, name, messages, max_length=None):
|
||||
if max_length is not None and len(text) > max_length:
|
||||
messages.append(f'Error: {name} is too long')
|
||||
messages.append(f"Error: {name} is too long")
|
||||
return False
|
||||
if len(text) > 0 and all(c.isalnum() or c.isspace() for c in text) is False:
|
||||
messages.append(f'Error: {name} must consist of only letters and digits')
|
||||
messages.append(f"Error: {name} must consist of only letters and digits")
|
||||
return False
|
||||
return True
|
||||
|
|
|
@ -39,7 +39,7 @@ class LockedCoinError(Exception):
|
|||
self.coinid = coinid
|
||||
|
||||
def __str__(self):
|
||||
return 'Coin must be unlocked: ' + str(self.coinid)
|
||||
return "Coin must be unlocked: " + str(self.coinid)
|
||||
|
||||
|
||||
def ensure(v, err_string):
|
||||
|
@ -50,7 +50,7 @@ def ensure(v, err_string):
|
|||
def toBool(s) -> bool:
|
||||
if isinstance(s, bool):
|
||||
return s
|
||||
return s.lower() in ['1', 'true']
|
||||
return s.lower() in ["1", "true"]
|
||||
|
||||
|
||||
def jsonDecimal(obj):
|
||||
|
@ -76,8 +76,8 @@ def SerialiseNum(n: int) -> bytes:
|
|||
rv = bytearray()
|
||||
neg = n < 0
|
||||
absvalue = -n if neg else n
|
||||
while (absvalue):
|
||||
rv.append(absvalue & 0xff)
|
||||
while absvalue:
|
||||
rv.append(absvalue & 0xFF)
|
||||
absvalue >>= 8
|
||||
if rv[-1] & 0x80:
|
||||
rv.append(0x80 if neg else 0)
|
||||
|
@ -106,34 +106,36 @@ def DeserialiseNum(b: bytes, o: int = 0) -> int:
|
|||
def float_to_str(f: float) -> str:
|
||||
# stackoverflow.com/questions/38847690
|
||||
d1 = decimal_ctx.create_decimal(repr(f))
|
||||
return format(d1, 'f')
|
||||
return format(d1, "f")
|
||||
|
||||
|
||||
def make_int(v, scale: int = 8, r: int = 0) -> int: # r = 0, no rounding (fail), r > 0 round off, r < 0 floor
|
||||
def make_int(
|
||||
v, scale: int = 8, r: int = 0
|
||||
) -> int: # r = 0, no rounding (fail), r > 0 round off, r < 0 floor
|
||||
if isinstance(v, float):
|
||||
v = float_to_str(v)
|
||||
elif isinstance(v, int):
|
||||
return v * 10 ** scale
|
||||
return v * 10**scale
|
||||
|
||||
sign = 1
|
||||
if v[0] == '-':
|
||||
if v[0] == "-":
|
||||
v = v[1:]
|
||||
sign = -1
|
||||
ep = 10 ** scale
|
||||
ep = 10**scale
|
||||
have_dp = False
|
||||
rv = 0
|
||||
for c in v:
|
||||
if c == '.':
|
||||
if c == ".":
|
||||
rv *= ep
|
||||
have_dp = True
|
||||
continue
|
||||
if not c.isdigit():
|
||||
raise ValueError('Invalid char: ' + c)
|
||||
raise ValueError("Invalid char: " + c)
|
||||
if have_dp:
|
||||
ep //= 10
|
||||
if ep <= 0:
|
||||
if r == 0:
|
||||
raise ValueError('Mantissa too long')
|
||||
raise ValueError("Mantissa too long")
|
||||
if r > 0:
|
||||
# Round off
|
||||
if int(c) > 4:
|
||||
|
@ -151,51 +153,53 @@ def validate_amount(amount, scale: int = 8) -> bool:
|
|||
str_amount = float_to_str(amount) if isinstance(amount, float) else str(amount)
|
||||
has_decimal = False
|
||||
for c in str_amount:
|
||||
if c == '.' and not has_decimal:
|
||||
if c == "." and not has_decimal:
|
||||
has_decimal = True
|
||||
continue
|
||||
if not c.isdigit():
|
||||
raise ValueError('Invalid amount')
|
||||
raise ValueError("Invalid amount")
|
||||
|
||||
ar = str_amount.split('.')
|
||||
ar = str_amount.split(".")
|
||||
if len(ar) > 1 and len(ar[1]) > scale:
|
||||
raise ValueError('Too many decimal places in amount {}'.format(str_amount))
|
||||
raise ValueError("Too many decimal places in amount {}".format(str_amount))
|
||||
return True
|
||||
|
||||
|
||||
def format_amount(i: int, display_scale: int, scale: int = None) -> str:
|
||||
if not isinstance(i, int):
|
||||
raise ValueError('Amount must be an integer.') # Raise error instead of converting as amounts should always be integers
|
||||
raise ValueError(
|
||||
"Amount must be an integer."
|
||||
) # Raise error instead of converting as amounts should always be integers
|
||||
if scale is None:
|
||||
scale = display_scale
|
||||
ep = 10 ** scale
|
||||
ep = 10**scale
|
||||
n = abs(i)
|
||||
quotient = n // ep
|
||||
remainder = n % ep
|
||||
if display_scale != scale:
|
||||
remainder %= (10 ** display_scale)
|
||||
rv = '{}.{:0>{scale}}'.format(quotient, remainder, scale=display_scale)
|
||||
remainder %= 10**display_scale
|
||||
rv = "{}.{:0>{scale}}".format(quotient, remainder, scale=display_scale)
|
||||
if i < 0:
|
||||
rv = '-' + rv
|
||||
rv = "-" + rv
|
||||
return rv
|
||||
|
||||
|
||||
def format_timestamp(value: int, with_seconds: bool = False) -> str:
|
||||
str_format = '%Y-%m-%d %H:%M'
|
||||
str_format = "%Y-%m-%d %H:%M"
|
||||
if with_seconds:
|
||||
str_format += ':%S'
|
||||
str_format += ' %z'
|
||||
str_format += ":%S"
|
||||
str_format += " %z"
|
||||
return time.strftime(str_format, time.localtime(value))
|
||||
|
||||
|
||||
def b2i(b: bytes) -> int:
|
||||
# bytes32ToInt
|
||||
return int.from_bytes(b, byteorder='big')
|
||||
return int.from_bytes(b, byteorder="big")
|
||||
|
||||
|
||||
def i2b(i: int) -> bytes:
|
||||
# intToBytes32
|
||||
return i.to_bytes(32, byteorder='big')
|
||||
return i.to_bytes(32, byteorder="big")
|
||||
|
||||
|
||||
def b2h(b: bytes) -> str:
|
||||
|
@ -203,7 +207,7 @@ def b2h(b: bytes) -> str:
|
|||
|
||||
|
||||
def h2b(h: str) -> bytes:
|
||||
if h.startswith('0x'):
|
||||
if h.startswith("0x"):
|
||||
h = h[2:]
|
||||
return bytes.fromhex(h)
|
||||
|
||||
|
@ -220,5 +224,5 @@ def zeroIfNone(value) -> int:
|
|||
|
||||
def hex_or_none(value: bytes) -> str:
|
||||
if value is None:
|
||||
return 'None'
|
||||
return "None"
|
||||
return value.hex()
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
from basicswap.contrib.segwit_addr import bech32_decode, convertbits, bech32_encode
|
||||
from basicswap.util.crypto import ripemd160, sha256
|
||||
|
||||
__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
__b58chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
|
||||
|
||||
def b58decode(v, length=None):
|
||||
long_value = 0
|
||||
for (i, c) in enumerate(v[::-1]):
|
||||
for i, c in enumerate(v[::-1]):
|
||||
ofs = __b58chars.find(c)
|
||||
if ofs < 0:
|
||||
return None
|
||||
|
@ -38,10 +38,10 @@ def b58decode(v, length=None):
|
|||
|
||||
def b58encode(v):
|
||||
long_value = 0
|
||||
for (i, c) in enumerate(v[::-1]):
|
||||
for i, c in enumerate(v[::-1]):
|
||||
long_value += (256**i) * c
|
||||
|
||||
result = ''
|
||||
result = ""
|
||||
while long_value >= 58:
|
||||
div, mod = divmod(long_value, 58)
|
||||
result = __b58chars[mod] + result
|
||||
|
@ -58,7 +58,9 @@ def b58encode(v):
|
|||
return (__b58chars[0] * nPad) + result
|
||||
|
||||
|
||||
def encodeStealthAddress(prefix_byte: int, scan_pubkey: bytes, spend_pubkey: bytes) -> str:
|
||||
def encodeStealthAddress(
|
||||
prefix_byte: int, scan_pubkey: bytes, spend_pubkey: bytes
|
||||
) -> str:
|
||||
data = bytes((0x00,))
|
||||
data += scan_pubkey
|
||||
data += bytes((0x01,))
|
||||
|
@ -114,7 +116,7 @@ def decodeAddress(address: str) -> bytes:
|
|||
prefixed_data = addr_data[:-4]
|
||||
checksum = addr_data[-4:]
|
||||
if sha256(sha256(prefixed_data))[:4] != checksum:
|
||||
raise ValueError('Checksum mismatch')
|
||||
raise ValueError("Checksum mismatch")
|
||||
return prefixed_data
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ from basicswap.contrib.ellipticcurve import CurveFp, Point, INFINITY, jacobi_sym
|
|||
from . import i2b
|
||||
|
||||
|
||||
class ECCParameters():
|
||||
class ECCParameters:
|
||||
def __init__(self, p, a, b, Gx, Gy, o):
|
||||
self.p = p
|
||||
self.a = a
|
||||
|
@ -20,12 +20,13 @@ class ECCParameters():
|
|||
|
||||
|
||||
ep = ECCParameters(
|
||||
p=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
|
||||
p=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,
|
||||
a=0x0,
|
||||
b=0x7,
|
||||
Gx=0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
|
||||
Gy=0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8,
|
||||
o=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141)
|
||||
Gx=0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
|
||||
Gy=0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8,
|
||||
o=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,
|
||||
)
|
||||
|
||||
|
||||
curve_secp256k1 = CurveFp(ep.p, ep.a, ep.b)
|
||||
|
@ -34,7 +35,11 @@ SECP256K1_ORDER_HALF = ep.o // 2
|
|||
|
||||
|
||||
def ToDER(P) -> bytes:
|
||||
return bytes((4, )) + int(P.x()).to_bytes(32, byteorder='big') + int(P.y()).to_bytes(32, byteorder='big')
|
||||
return (
|
||||
bytes((4,))
|
||||
+ int(P.x()).to_bytes(32, byteorder="big")
|
||||
+ int(P.y()).to_bytes(32, byteorder="big")
|
||||
)
|
||||
|
||||
|
||||
def getSecretBytes() -> bytes:
|
||||
|
@ -50,7 +55,7 @@ def getInsecureBytes() -> bytes:
|
|||
while True:
|
||||
s = os.urandom(32)
|
||||
|
||||
s_test = int.from_bytes(s, byteorder='big')
|
||||
s_test = int.from_bytes(s, byteorder="big")
|
||||
if s_test > 1 and s_test < ep.o:
|
||||
return s
|
||||
|
||||
|
@ -59,7 +64,7 @@ def getInsecureInt() -> int:
|
|||
while True:
|
||||
s = os.urandom(32)
|
||||
|
||||
s_test = int.from_bytes(s, byteorder='big')
|
||||
s_test = int.from_bytes(s, byteorder="big")
|
||||
if s_test > 1 and s_test < ep.o:
|
||||
return s_test
|
||||
|
||||
|
@ -77,7 +82,7 @@ def powMod(x, y, z) -> int:
|
|||
|
||||
|
||||
def ExpandPoint(xb, sign):
|
||||
x = int.from_bytes(xb, byteorder='big')
|
||||
x = int.from_bytes(xb, byteorder="big")
|
||||
a = (powMod(x, 3, ep.p) + 7) % ep.p
|
||||
y = powMod(a, (ep.p + 1) // 4, ep.p)
|
||||
|
||||
|
@ -89,7 +94,7 @@ def ExpandPoint(xb, sign):
|
|||
def CPKToPoint(cpk):
|
||||
y_parity = cpk[0] - 2
|
||||
|
||||
x = int.from_bytes(cpk[1:], byteorder='big')
|
||||
x = int.from_bytes(cpk[1:], byteorder="big")
|
||||
a = (powMod(x, 3, ep.p) + 7) % ep.p
|
||||
y = powMod(a, (ep.p + 1) // 4, ep.p)
|
||||
|
||||
|
@ -102,28 +107,29 @@ def CPKToPoint(cpk):
|
|||
def pointToCPK2(point, ind=0x09):
|
||||
# The function is_square(x), where x is an integer, returns whether or not x is a quadratic residue modulo p. Since p is prime, it is equivalent to the Legendre symbol (x / p) = x(p-1)/2 mod p being equal to 1[8].
|
||||
ind = bytes((ind ^ (1 if jacobi_symbol(point.y(), ep.p) == 1 else 0),))
|
||||
return ind + point.x().to_bytes(32, byteorder='big')
|
||||
return ind + point.x().to_bytes(32, byteorder="big")
|
||||
|
||||
|
||||
def pointToCPK(point):
|
||||
|
||||
y = point.y().to_bytes(32, byteorder='big')
|
||||
y = point.y().to_bytes(32, byteorder="big")
|
||||
ind = bytes((0x03,)) if y[31] % 2 else bytes((0x02,))
|
||||
|
||||
cpk = ind + point.x().to_bytes(32, byteorder='big')
|
||||
cpk = ind + point.x().to_bytes(32, byteorder="big")
|
||||
return cpk
|
||||
|
||||
|
||||
def secretToCPK(secret):
|
||||
secretInt = secret if isinstance(secret, int) \
|
||||
else int.from_bytes(secret, byteorder='big')
|
||||
secretInt = (
|
||||
secret if isinstance(secret, int) else int.from_bytes(secret, byteorder="big")
|
||||
)
|
||||
|
||||
R = G * secretInt
|
||||
|
||||
Y = R.y().to_bytes(32, byteorder='big')
|
||||
Y = R.y().to_bytes(32, byteorder="big")
|
||||
ind = bytes((0x03,)) if Y[31] % 2 else bytes((0x02,))
|
||||
|
||||
pubkey = ind + R.x().to_bytes(32, byteorder='big')
|
||||
pubkey = ind + R.x().to_bytes(32, byteorder="big")
|
||||
|
||||
return pubkey
|
||||
|
||||
|
@ -136,7 +142,7 @@ def getKeypair():
|
|||
def hashToCurve(pubkey):
|
||||
|
||||
xBytes = hashlib.sha256(pubkey).digest()
|
||||
x = int.from_bytes(xBytes, byteorder='big')
|
||||
x = int.from_bytes(xBytes, byteorder="big")
|
||||
|
||||
for k in range(0, 100):
|
||||
# get matching y element for point
|
||||
|
@ -155,12 +161,14 @@ def hashToCurve(pubkey):
|
|||
x = (x + 1) % ep.p # % P?
|
||||
continue
|
||||
|
||||
if R == INFINITY or R * ep.o != INFINITY: # is R * O != INFINITY check necessary? Validation of Elliptic Curve Public Keys says no if cofactor = 1
|
||||
if (
|
||||
R == INFINITY or R * ep.o != INFINITY
|
||||
): # is R * O != INFINITY check necessary? Validation of Elliptic Curve Public Keys says no if cofactor = 1
|
||||
x = (x + 1) % ep.p # % P?
|
||||
continue
|
||||
return R
|
||||
|
||||
raise ValueError('hashToCurve failed for 100 tries')
|
||||
raise ValueError("hashToCurve failed for 100 tries")
|
||||
|
||||
|
||||
def hash256(inb):
|
||||
|
@ -168,23 +176,35 @@ def hash256(inb):
|
|||
|
||||
|
||||
def testEccUtils():
|
||||
print('testEccUtils()')
|
||||
print("testEccUtils()")
|
||||
|
||||
G_enc = ToDER(G)
|
||||
assert (G_enc.hex() == '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8')
|
||||
assert (
|
||||
G_enc.hex()
|
||||
== "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"
|
||||
)
|
||||
|
||||
G_enc = pointToCPK(G)
|
||||
assert (G_enc.hex() == '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')
|
||||
assert (
|
||||
G_enc.hex()
|
||||
== "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
|
||||
)
|
||||
G_dec = CPKToPoint(G_enc)
|
||||
assert (G_dec == G)
|
||||
assert G_dec == G
|
||||
|
||||
G_enc = pointToCPK2(G)
|
||||
assert (G_enc.hex() == '0879be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')
|
||||
assert (
|
||||
G_enc.hex()
|
||||
== "0879be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
|
||||
)
|
||||
|
||||
H = hashToCurve(ToDER(G))
|
||||
assert (pointToCPK(H).hex() == '0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0')
|
||||
assert (
|
||||
pointToCPK(H).hex()
|
||||
== "0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"
|
||||
)
|
||||
|
||||
print('Passed.')
|
||||
print("Passed.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -7,21 +7,30 @@
|
|||
|
||||
from .crypto import blake256, hash160, hmac_sha512, ripemd160
|
||||
|
||||
from coincurve.keys import (
|
||||
PrivateKey,
|
||||
PublicKey)
|
||||
from coincurve.keys import PrivateKey, PublicKey
|
||||
|
||||
|
||||
def BIP32Hash(chaincode: bytes, child_no: int, key_data_type: int, keydata: bytes):
|
||||
return hmac_sha512(chaincode, key_data_type.to_bytes(1, 'big') + keydata + child_no.to_bytes(4, 'big'))
|
||||
return hmac_sha512(
|
||||
chaincode,
|
||||
key_data_type.to_bytes(1, "big") + keydata + child_no.to_bytes(4, "big"),
|
||||
)
|
||||
|
||||
|
||||
def hash160_dcr(data: bytes) -> bytes:
|
||||
return ripemd160(blake256(data))
|
||||
|
||||
|
||||
class ExtKeyPair():
|
||||
__slots__ = ('_depth', '_fingerprint', '_child_no', '_chaincode', '_key', '_pubkey', 'hash_func')
|
||||
class ExtKeyPair:
|
||||
__slots__ = (
|
||||
"_depth",
|
||||
"_fingerprint",
|
||||
"_child_no",
|
||||
"_chaincode",
|
||||
"_key",
|
||||
"_pubkey",
|
||||
"hash_func",
|
||||
)
|
||||
|
||||
def __init__(self, coin_type=1):
|
||||
if coin_type == 4:
|
||||
|
@ -30,20 +39,20 @@ class ExtKeyPair():
|
|||
self.hash_func = hash160
|
||||
|
||||
def set_seed(self, seed: bytes) -> None:
|
||||
hashout: bytes = hmac_sha512(b'Bitcoin seed', seed)
|
||||
hashout: bytes = hmac_sha512(b"Bitcoin seed", seed)
|
||||
self._key = hashout[:32]
|
||||
self._pubkey = None
|
||||
self._chaincode = hashout[32:]
|
||||
self._depth = 0
|
||||
self._child_no = 0
|
||||
self._fingerprint = b'\0' * 4
|
||||
self._fingerprint = b"\0" * 4
|
||||
|
||||
def has_key(self) -> bool:
|
||||
return False if self._key is None else True
|
||||
|
||||
def neuter(self) -> None:
|
||||
if self._key is None:
|
||||
raise ValueError('Already neutered')
|
||||
raise ValueError("Already neutered")
|
||||
self._pubkey = PublicKey.from_secret(self._key).format()
|
||||
self._key = None
|
||||
|
||||
|
@ -74,7 +83,11 @@ class ExtKeyPair():
|
|||
out._pubkey = K.format()
|
||||
else:
|
||||
k = PrivateKey(self._key)
|
||||
out._fingerprint = self.hash_func(self._pubkey if self._pubkey else PublicKey.from_secret(self._key).format())[:4]
|
||||
out._fingerprint = self.hash_func(
|
||||
self._pubkey
|
||||
if self._pubkey
|
||||
else PublicKey.from_secret(self._key).format()
|
||||
)[:4]
|
||||
new_hash = BIP32Hash(self._chaincode, child_no, 0, self._key)
|
||||
out._chaincode = new_hash[32:]
|
||||
k.add(new_hash[:32], update=True)
|
||||
|
@ -85,27 +98,35 @@ class ExtKeyPair():
|
|||
return out
|
||||
|
||||
def encode_v(self) -> bytes:
|
||||
return self._depth.to_bytes(1, 'big') + \
|
||||
self._fingerprint + \
|
||||
self._child_no.to_bytes(4, 'big') + \
|
||||
self._chaincode + \
|
||||
b'\x00' + \
|
||||
self._key
|
||||
return (
|
||||
self._depth.to_bytes(1, "big")
|
||||
+ self._fingerprint
|
||||
+ self._child_no.to_bytes(4, "big")
|
||||
+ self._chaincode
|
||||
+ b"\x00"
|
||||
+ self._key
|
||||
)
|
||||
|
||||
def encode_p(self) -> bytes:
|
||||
pubkey = PublicKey.from_secret(self._key).format() if self._pubkey is None else self._pubkey
|
||||
return self._depth.to_bytes(1, 'big') + \
|
||||
self._fingerprint + \
|
||||
self._child_no.to_bytes(4, 'big') + \
|
||||
self._chaincode + \
|
||||
pubkey
|
||||
pubkey = (
|
||||
PublicKey.from_secret(self._key).format()
|
||||
if self._pubkey is None
|
||||
else self._pubkey
|
||||
)
|
||||
return (
|
||||
self._depth.to_bytes(1, "big")
|
||||
+ self._fingerprint
|
||||
+ self._child_no.to_bytes(4, "big")
|
||||
+ self._chaincode
|
||||
+ pubkey
|
||||
)
|
||||
|
||||
def decode(self, data: bytes) -> None:
|
||||
if len(data) != 74:
|
||||
raise ValueError('Unexpected extkey length')
|
||||
raise ValueError("Unexpected extkey length")
|
||||
self._depth = data[0]
|
||||
self._fingerprint = data[1:5]
|
||||
self._child_no = int.from_bytes(data[5:9], 'big')
|
||||
self._child_no = int.from_bytes(data[5:9], "big")
|
||||
self._chaincode = data[9:41]
|
||||
|
||||
if data[41] == 0:
|
||||
|
|
|
@ -7,25 +7,25 @@
|
|||
|
||||
def decode_compactsize(b: bytes, offset: int = 0) -> (int, int):
|
||||
i = b[offset]
|
||||
if i < 0xfd:
|
||||
if i < 0xFD:
|
||||
return i, 1
|
||||
offset += 1
|
||||
if i == 0xfd:
|
||||
return int.from_bytes(b[offset: offset + 2], 'little'), 3
|
||||
if i == 0xfe:
|
||||
return int.from_bytes(b[offset: offset + 4], 'little'), 5
|
||||
if i == 0xFD:
|
||||
return int.from_bytes(b[offset : offset + 2], "little"), 3
|
||||
if i == 0xFE:
|
||||
return int.from_bytes(b[offset : offset + 4], "little"), 5
|
||||
# 0xff
|
||||
return int.from_bytes(b[offset: offset + 8], 'little'), 9
|
||||
return int.from_bytes(b[offset : offset + 8], "little"), 9
|
||||
|
||||
|
||||
def encode_compactsize(i: int) -> bytes:
|
||||
if i < 0xfd:
|
||||
if i < 0xFD:
|
||||
return bytes((i,))
|
||||
if i <= 0xffff:
|
||||
return bytes((0xfd,)) + i.to_bytes(2, 'little')
|
||||
if i <= 0xffffffff:
|
||||
return bytes((0xfe,)) + i.to_bytes(4, 'little')
|
||||
return bytes((0xff,)) + i.to_bytes(8, 'little')
|
||||
if i <= 0xFFFF:
|
||||
return bytes((0xFD,)) + i.to_bytes(2, "little")
|
||||
if i <= 0xFFFFFFFF:
|
||||
return bytes((0xFE,)) + i.to_bytes(4, "little")
|
||||
return bytes((0xFF,)) + i.to_bytes(8, "little")
|
||||
|
||||
|
||||
def decode_varint(b: bytes, offset: int = 0) -> (int, int):
|
||||
|
@ -38,7 +38,7 @@ def decode_varint(b: bytes, offset: int = 0) -> (int, int):
|
|||
if not c & 0x80:
|
||||
break
|
||||
if num_bytes > 8:
|
||||
raise ValueError('Too many bytes')
|
||||
raise ValueError("Too many bytes")
|
||||
return i, num_bytes
|
||||
|
||||
|
||||
|
@ -46,6 +46,6 @@ def encode_varint(i: int) -> bytes:
|
|||
b = bytearray()
|
||||
while i > 0x7F:
|
||||
b += bytes(((i & 0x7F) | 0x80,))
|
||||
i = (i >> 7)
|
||||
i = i >> 7
|
||||
b += bytes((i,))
|
||||
return b
|
||||
|
|
|
@ -15,9 +15,9 @@ from urllib.request import Request, urlopen
|
|||
|
||||
|
||||
def is_private_ip_address(addr: str):
|
||||
if addr == 'localhost':
|
||||
if addr == "localhost":
|
||||
return True
|
||||
if addr.endswith('.local'):
|
||||
if addr.endswith(".local"):
|
||||
return True
|
||||
try:
|
||||
return ipaddress.ip_address(addr).is_private
|
||||
|
@ -27,7 +27,7 @@ def is_private_ip_address(addr: str):
|
|||
|
||||
def make_reporthook(read_start: int, logger):
|
||||
read = read_start # Number of bytes read so far
|
||||
last_percent_str = ''
|
||||
last_percent_str = ""
|
||||
time_last = time.time()
|
||||
read_last = read_start
|
||||
display_last = time_last
|
||||
|
@ -35,7 +35,7 @@ def make_reporthook(read_start: int, logger):
|
|||
average_buffer = [-1] * 8
|
||||
|
||||
if read_start > 0:
|
||||
logger.info(f'Attempting to resume from byte {read_start}')
|
||||
logger.info(f"Attempting to resume from byte {read_start}")
|
||||
|
||||
def reporthook(blocknum, blocksize, totalsize):
|
||||
nonlocal read, last_percent_str, time_last, read_last, display_last, read_start
|
||||
|
@ -72,32 +72,32 @@ def make_reporthook(read_start: int, logger):
|
|||
average_bits_per_second /= samples
|
||||
|
||||
speed_str: str
|
||||
if average_bits_per_second > 1000 ** 3:
|
||||
speed_str = '{:.2f} Gbps'.format(average_bits_per_second / (1000 ** 3))
|
||||
elif average_bits_per_second > 1000 ** 2:
|
||||
speed_str = '{:.2f} Mbps'.format(average_bits_per_second / (1000 ** 2))
|
||||
if average_bits_per_second > 1000**3:
|
||||
speed_str = "{:.2f} Gbps".format(average_bits_per_second / (1000**3))
|
||||
elif average_bits_per_second > 1000**2:
|
||||
speed_str = "{:.2f} Mbps".format(average_bits_per_second / (1000**2))
|
||||
else:
|
||||
speed_str = '{:.2f} kbps'.format(average_bits_per_second / 1000)
|
||||
speed_str = "{:.2f} kbps".format(average_bits_per_second / 1000)
|
||||
|
||||
if totalsize > 0:
|
||||
percent_str = '%5.0f%%' % (read * 1e2 / use_size)
|
||||
percent_str = "%5.0f%%" % (read * 1e2 / use_size)
|
||||
if percent_str != last_percent_str or time_now - display_last > 10:
|
||||
logger.info(percent_str + ' ' + speed_str)
|
||||
logger.info(percent_str + " " + speed_str)
|
||||
last_percent_str = percent_str
|
||||
display_last = time_now
|
||||
else:
|
||||
logger.info(f'Read {read}, {speed_str}')
|
||||
logger.info(f"Read {read}, {speed_str}")
|
||||
|
||||
return reporthook
|
||||
|
||||
|
||||
def urlretrieve(url, filename, reporthook=None, data=None, resume_from=0):
|
||||
'''urlretrieve with resume
|
||||
'''
|
||||
"""urlretrieve with resume"""
|
||||
url_type, path = _splittype(url)
|
||||
|
||||
req = Request(url)
|
||||
if resume_from > 0:
|
||||
req.add_header('Range', f'bytes={resume_from}-')
|
||||
req.add_header("Range", f"bytes={resume_from}-")
|
||||
with contextlib.closing(urlopen(req)) as fp:
|
||||
headers = fp.info()
|
||||
|
||||
|
@ -106,7 +106,7 @@ def urlretrieve(url, filename, reporthook=None, data=None, resume_from=0):
|
|||
if url_type == "file" and not filename:
|
||||
return os.path.normpath(path), headers
|
||||
|
||||
with open(filename, 'ab' if resume_from > 0 else 'wb') as tfp:
|
||||
with open(filename, "ab" if resume_from > 0 else "wb") as tfp:
|
||||
result = filename, headers
|
||||
bs = 1024 * 8
|
||||
size = -1
|
||||
|
@ -117,10 +117,10 @@ def urlretrieve(url, filename, reporthook=None, data=None, resume_from=0):
|
|||
size = int(headers["Content-Length"])
|
||||
if "Content-Range" in headers:
|
||||
range_str = headers["Content-Range"]
|
||||
offset = range_str.find('-')
|
||||
offset = range_str.find("-")
|
||||
range_from = int(range_str[6:offset])
|
||||
if resume_from != range_from:
|
||||
raise ValueError('Download is not resuming from the expected byte')
|
||||
raise ValueError("Download is not resuming from the expected byte")
|
||||
|
||||
if reporthook:
|
||||
reporthook(blocknum, bs, size)
|
||||
|
@ -137,7 +137,7 @@ def urlretrieve(url, filename, reporthook=None, data=None, resume_from=0):
|
|||
|
||||
if size >= 0 and read < size:
|
||||
raise ContentTooShortError(
|
||||
"retrieval incomplete: got only %i out of %i bytes"
|
||||
% (read, size), result)
|
||||
"retrieval incomplete: got only %i out of %i bytes" % (read, size), result
|
||||
)
|
||||
|
||||
return result
|
||||
|
|
|
@ -16,7 +16,7 @@ def rfc2440_hash_password(password, salt=None):
|
|||
salt = secrets.token_bytes(8)
|
||||
assert len(salt) == 8
|
||||
|
||||
hashbytes = salt + password.encode('utf-8')
|
||||
hashbytes = salt + password.encode("utf-8")
|
||||
len_hashbytes = len(hashbytes)
|
||||
h = hashlib.sha1()
|
||||
|
||||
|
@ -27,5 +27,5 @@ def rfc2440_hash_password(password, salt=None):
|
|||
continue
|
||||
h.update(hashbytes[:count])
|
||||
break
|
||||
rv = '16:' + salt.hex() + '60' + h.hexdigest()
|
||||
rv = "16:" + salt.hex() + "60" + h.hexdigest()
|
||||
return rv.upper()
|
||||
|
|
|
@ -6,7 +6,13 @@
|
|||
|
||||
import struct
|
||||
import hashlib
|
||||
from basicswap.contrib.test_framework.script import OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4, CScriptInvalidError, CScriptTruncatedPushDataError
|
||||
from basicswap.contrib.test_framework.script import (
|
||||
OP_PUSHDATA1,
|
||||
OP_PUSHDATA2,
|
||||
OP_PUSHDATA4,
|
||||
CScriptInvalidError,
|
||||
CScriptTruncatedPushDataError,
|
||||
)
|
||||
from basicswap.script import OpCodes
|
||||
|
||||
|
||||
|
@ -17,15 +23,15 @@ def decodeScriptNum(script_bytes, o):
|
|||
return ((num_len - OpCodes.OP_1) + 1, 1)
|
||||
|
||||
if num_len > 4:
|
||||
raise ValueError('Bad scriptnum length') # Max 4 bytes
|
||||
raise ValueError("Bad scriptnum length") # Max 4 bytes
|
||||
if num_len + o >= len(script_bytes):
|
||||
raise ValueError('Bad script length')
|
||||
raise ValueError("Bad script length")
|
||||
o += 1
|
||||
for i in range(num_len):
|
||||
b = script_bytes[o + i]
|
||||
# Negative flag set in last byte, if num is positive and > 0x80 an extra 0x00 byte will be appended
|
||||
if i == num_len - 1 and b & 0x80:
|
||||
b &= (~(0x80) & 0xFF)
|
||||
b &= ~(0x80) & 0xFF
|
||||
v += int(b) << 8 * i
|
||||
v *= -1
|
||||
else:
|
||||
|
@ -41,47 +47,50 @@ def decodePushData(script_bytes, o):
|
|||
i += 1
|
||||
|
||||
if opcode < OP_PUSHDATA1:
|
||||
pushdata_type = 'PUSHDATA(%d)' % opcode
|
||||
pushdata_type = "PUSHDATA(%d)" % opcode
|
||||
datasize = opcode
|
||||
|
||||
elif opcode == OP_PUSHDATA1:
|
||||
pushdata_type = 'PUSHDATA1'
|
||||
pushdata_type = "PUSHDATA1"
|
||||
if i >= len(script_bytes):
|
||||
raise CScriptInvalidError('PUSHDATA1: missing data length')
|
||||
raise CScriptInvalidError("PUSHDATA1: missing data length")
|
||||
datasize = script_bytes[i]
|
||||
i += 1
|
||||
|
||||
elif opcode == OP_PUSHDATA2:
|
||||
pushdata_type = 'PUSHDATA2'
|
||||
pushdata_type = "PUSHDATA2"
|
||||
if i + 1 >= len(script_bytes):
|
||||
raise CScriptInvalidError('PUSHDATA2: missing data length')
|
||||
raise CScriptInvalidError("PUSHDATA2: missing data length")
|
||||
datasize = script_bytes[i] + (script_bytes[i + 1] << 8)
|
||||
i += 2
|
||||
|
||||
elif opcode == OP_PUSHDATA4:
|
||||
pushdata_type = 'PUSHDATA4'
|
||||
pushdata_type = "PUSHDATA4"
|
||||
if i + 3 >= len(script_bytes):
|
||||
raise CScriptInvalidError('PUSHDATA4: missing data length')
|
||||
datasize = script_bytes[i] + (script_bytes[i + 1] << 8) + (script_bytes[i + 2] << 16) + (script_bytes[i + 3] << 24)
|
||||
raise CScriptInvalidError("PUSHDATA4: missing data length")
|
||||
datasize = (
|
||||
script_bytes[i]
|
||||
+ (script_bytes[i + 1] << 8)
|
||||
+ (script_bytes[i + 2] << 16)
|
||||
+ (script_bytes[i + 3] << 24)
|
||||
)
|
||||
i += 4
|
||||
|
||||
else:
|
||||
assert False # shouldn't happen
|
||||
|
||||
data = bytes(script_bytes[i:i + datasize])
|
||||
data = bytes(script_bytes[i : i + datasize])
|
||||
|
||||
# Check for truncation
|
||||
if len(data) < datasize:
|
||||
raise CScriptTruncatedPushDataError('%s: truncated data' % pushdata_type, data)
|
||||
raise CScriptTruncatedPushDataError("%s: truncated data" % pushdata_type, data)
|
||||
|
||||
# return data and the number of bytes to skip forward
|
||||
return (data, i + datasize - o)
|
||||
|
||||
|
||||
def getP2SHScriptForHash(p2sh):
|
||||
return bytes((OpCodes.OP_HASH160, 0x14)) \
|
||||
+ p2sh \
|
||||
+ bytes((OpCodes.OP_EQUAL,))
|
||||
return bytes((OpCodes.OP_HASH160, 0x14)) + p2sh + bytes((OpCodes.OP_EQUAL,))
|
||||
|
||||
|
||||
def getP2WSH(script):
|
||||
|
@ -91,26 +100,26 @@ def getP2WSH(script):
|
|||
def SerialiseNumCompact(v):
|
||||
if v < 253:
|
||||
return bytes((v,))
|
||||
if v <= 0xffff: # USHRT_MAX
|
||||
if v <= 0xFFFF: # USHRT_MAX
|
||||
return struct.pack("<BH", 253, v)
|
||||
if v <= 0xffffffff: # UINT_MAX
|
||||
if v <= 0xFFFFFFFF: # UINT_MAX
|
||||
return struct.pack("<BI", 254, v)
|
||||
if v <= 0xffffffffffffffff: # UINT_MAX
|
||||
if v <= 0xFFFFFFFFFFFFFFFF: # UINT_MAX
|
||||
return struct.pack("<BQ", 255, v)
|
||||
raise ValueError('Value too large')
|
||||
raise ValueError("Value too large")
|
||||
|
||||
|
||||
def getCompactSizeLen(v):
|
||||
# Compact Size
|
||||
if v < 253:
|
||||
return 1
|
||||
if v <= 0xffff: # USHRT_MAX
|
||||
if v <= 0xFFFF: # USHRT_MAX
|
||||
return 3
|
||||
if v <= 0xffffffff: # UINT_MAX
|
||||
if v <= 0xFFFFFFFF: # UINT_MAX
|
||||
return 5
|
||||
if v <= 0xffffffffffffffff: # UINT_MAX
|
||||
if v <= 0xFFFFFFFFFFFFFFFF: # UINT_MAX
|
||||
return 9
|
||||
raise ValueError('Value too large')
|
||||
raise ValueError("Value too large")
|
||||
|
||||
|
||||
def getWitnessElementLen(v):
|
||||
|
|
|
@ -7,13 +7,15 @@ from .contrib.MoneroPy.base58 import encode as xmr_b58encode
|
|||
|
||||
def cn_fast_hash(s):
|
||||
k = Keccak.Keccak()
|
||||
return k.Keccak((len(s) * 8, s.hex()), 1088, 512, 32 * 8, False).lower() # r = bitrate = 1088, c = capacity, n = output length in bits
|
||||
return k.Keccak(
|
||||
(len(s) * 8, s.hex()), 1088, 512, 32 * 8, False
|
||||
).lower() # r = bitrate = 1088, c = capacity, n = output length in bits
|
||||
|
||||
|
||||
def encode_address(view_point: bytes, spend_point: bytes, version=18) -> str:
|
||||
prefix_bytes = version if isinstance(version, bytes) else encode_varint(version)
|
||||
buf = prefix_bytes + spend_point + view_point
|
||||
h = cn_fast_hash(buf)
|
||||
buf = buf + bytes.fromhex(h[0: 8])
|
||||
buf = buf + bytes.fromhex(h[0:8])
|
||||
|
||||
return xmr_b58encode(buf.hex())
|
||||
|
|
|
@ -11,17 +11,27 @@ import stat
|
|||
import subprocess
|
||||
import sys
|
||||
|
||||
STAT_0o775 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
|
||||
| stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
|
||||
| stat.S_IROTH | stat.S_IXOTH )
|
||||
STAT_0o775 = (
|
||||
stat.S_IRUSR
|
||||
| stat.S_IWUSR
|
||||
| stat.S_IXUSR
|
||||
| stat.S_IRGRP
|
||||
| stat.S_IWGRP
|
||||
| stat.S_IXGRP
|
||||
| stat.S_IROTH
|
||||
| stat.S_IXOTH
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
openssl_dir, openssl_cafile = os.path.split(
|
||||
ssl.get_default_verify_paths().openssl_cafile)
|
||||
ssl.get_default_verify_paths().openssl_cafile
|
||||
)
|
||||
|
||||
print(" -- pip install --upgrade certifi")
|
||||
subprocess.check_call([sys.executable,
|
||||
"-E", "-s", "-m", "pip", "install", "--upgrade", "certifi"])
|
||||
subprocess.check_call(
|
||||
[sys.executable, "-E", "-s", "-m", "pip", "install", "--upgrade", "certifi"]
|
||||
)
|
||||
|
||||
import certifi
|
||||
|
||||
|
@ -39,5 +49,6 @@ def main():
|
|||
os.chmod(openssl_cafile, STAT_0o775)
|
||||
print(" -- update complete")
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -9,91 +9,119 @@
|
|||
Join docker compose fragments
|
||||
"""
|
||||
|
||||
__version__ = '0.1'
|
||||
__version__ = "0.1"
|
||||
|
||||
import os
|
||||
import argparse
|
||||
|
||||
|
||||
def get_bkp_offset(filename, ext='yml'):
|
||||
def get_bkp_offset(filename, ext="yml"):
|
||||
for i in range(1000):
|
||||
if not os.path.exists(f'{filename}_bkp_{i}.{ext}'):
|
||||
if not os.path.exists(f"{filename}_bkp_{i}.{ext}"):
|
||||
return i
|
||||
raise ValueError(f'Unable to get backup filename for: {filename}.{ext}')
|
||||
raise ValueError(f"Unable to get backup filename for: {filename}.{ext}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('-v', '--version', action='version',
|
||||
version='%(prog)s {version}'.format(version=__version__))
|
||||
parser.add_argument('-c', '--coins', nargs='+', help='<Required> Select coins', required=True)
|
||||
parser.add_argument('--withscript', dest='withscript', help='Add container to run createoffers.py (default=false)', required=False, action='store_true')
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--version",
|
||||
action="version",
|
||||
version="%(prog)s {version}".format(version=__version__),
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c", "--coins", nargs="+", help="<Required> Select coins", required=True
|
||||
)
|
||||
parser.add_argument(
|
||||
"--withscript",
|
||||
dest="withscript",
|
||||
help="Add container to run createoffers.py (default=false)",
|
||||
required=False,
|
||||
action="store_true",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
with_coins = ['particl', ]
|
||||
with_coins = [
|
||||
"particl",
|
||||
]
|
||||
for coin_name in args.coins:
|
||||
parsed_name = coin_name.lower()
|
||||
if parsed_name not in with_coins:
|
||||
with_coins.append(parsed_name)
|
||||
|
||||
print('Preparing docker compose files with coins:', ','.join(with_coins))
|
||||
print("Preparing docker compose files with coins:", ",".join(with_coins))
|
||||
|
||||
num_docker_compose = get_bkp_offset('docker-compose')
|
||||
num_docker_compose_prepare = get_bkp_offset('docker-compose-prepare')
|
||||
num_docker_compose = get_bkp_offset("docker-compose")
|
||||
num_docker_compose_prepare = get_bkp_offset("docker-compose-prepare")
|
||||
|
||||
if os.path.exists('docker-compose.yml'):
|
||||
os.rename('docker-compose.yml', f'docker-compose_bkp_{num_docker_compose}.yml')
|
||||
if os.path.exists('docker-compose-prepare.yml'):
|
||||
os.rename('docker-compose-prepare.yml', f'docker-compose-prepare_bkp_{num_docker_compose_prepare}.yml')
|
||||
if os.path.exists("docker-compose.yml"):
|
||||
os.rename("docker-compose.yml", f"docker-compose_bkp_{num_docker_compose}.yml")
|
||||
if os.path.exists("docker-compose-prepare.yml"):
|
||||
os.rename(
|
||||
"docker-compose-prepare.yml",
|
||||
f"docker-compose-prepare_bkp_{num_docker_compose_prepare}.yml",
|
||||
)
|
||||
|
||||
fragments_dir = 'compose-fragments'
|
||||
with open('docker-compose.yml', 'wb') as fp, open('docker-compose-prepare.yml', 'wb') as fpp:
|
||||
with open(os.path.join(fragments_dir, '0_start.yml'), 'rb') as fp_in:
|
||||
fragments_dir = "compose-fragments"
|
||||
with (
|
||||
open("docker-compose.yml", "wb") as fp,
|
||||
open("docker-compose-prepare.yml", "wb") as fpp,
|
||||
):
|
||||
with open(os.path.join(fragments_dir, "0_start.yml"), "rb") as fp_in:
|
||||
for line in fp_in:
|
||||
fp.write(line)
|
||||
fpp.write(line)
|
||||
|
||||
for coin_name in with_coins:
|
||||
if coin_name == 'particl':
|
||||
if coin_name == "particl":
|
||||
# Nothing to do
|
||||
continue
|
||||
if coin_name in ('monero', 'wownero'):
|
||||
with open(os.path.join(fragments_dir, '1_{coin_name}-wallet.yml'), 'rb') as fp_in:
|
||||
if coin_name in ("monero", "wownero"):
|
||||
with open(
|
||||
os.path.join(fragments_dir, "1_{coin_name}-wallet.yml"), "rb"
|
||||
) as fp_in:
|
||||
for line in fp_in:
|
||||
fp.write(line)
|
||||
fpp.write(line)
|
||||
with open(os.path.join(fragments_dir, '8_{coin_name}-daemon.yml'), 'rb') as fp_in:
|
||||
with open(
|
||||
os.path.join(fragments_dir, "8_{coin_name}-daemon.yml"), "rb"
|
||||
) as fp_in:
|
||||
for line in fp_in:
|
||||
fp.write(line)
|
||||
continue
|
||||
if coin_name == 'decred':
|
||||
with open(os.path.join(fragments_dir, '1_decred-wallet.yml'), 'rb') as fp_in:
|
||||
if coin_name == "decred":
|
||||
with open(
|
||||
os.path.join(fragments_dir, "1_decred-wallet.yml"), "rb"
|
||||
) as fp_in:
|
||||
for line in fp_in:
|
||||
fp.write(line)
|
||||
fpp.write(line)
|
||||
with open(os.path.join(fragments_dir, '8_decred-daemon.yml'), 'rb') as fp_in:
|
||||
with open(
|
||||
os.path.join(fragments_dir, "8_decred-daemon.yml"), "rb"
|
||||
) as fp_in:
|
||||
for line in fp_in:
|
||||
fp.write(line)
|
||||
continue
|
||||
with open(os.path.join(fragments_dir, f'1_{coin_name}.yml'), 'rb') as fp_in:
|
||||
with open(os.path.join(fragments_dir, f"1_{coin_name}.yml"), "rb") as fp_in:
|
||||
for line in fp_in:
|
||||
fp.write(line)
|
||||
fpp.write(line)
|
||||
|
||||
with open(os.path.join(fragments_dir, '8_swapclient.yml'), 'rb') as fp_in:
|
||||
with open(os.path.join(fragments_dir, "8_swapclient.yml"), "rb") as fp_in:
|
||||
for line in fp_in:
|
||||
fp.write(line)
|
||||
|
||||
if args.withscript:
|
||||
with open(os.path.join(fragments_dir, '8_script.yml'), 'rb') as fp_in:
|
||||
with open(os.path.join(fragments_dir, "8_script.yml"), "rb") as fp_in:
|
||||
for line in fp_in:
|
||||
fp.write(line)
|
||||
|
||||
with open(os.path.join(fragments_dir, '9_swapprepare.yml'), 'rb') as fp_in:
|
||||
with open(os.path.join(fragments_dir, "9_swapprepare.yml"), "rb") as fp_in:
|
||||
for line in fp_in:
|
||||
fpp.write(line)
|
||||
print('Done.')
|
||||
print("Done.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -125,7 +125,7 @@ def checkForks(ro):
|
|||
assert ro["softforks"]["csv"]["active"]
|
||||
assert ro["softforks"]["segwit"]["active"]
|
||||
except Exception as e:
|
||||
logging.warning("Could not parse deployment info")
|
||||
logging.warning(f"Could not parse deployment info: {e}")
|
||||
|
||||
|
||||
def stopDaemons(daemons):
|
||||
|
|
Loading…
Reference in a new issue