mirror of
https://github.com/basicswap/basicswap.git
synced 2024-12-22 11:39:34 +00:00
wownero: integration
This commit is contained in:
parent
c3cd1871ef
commit
ee2f462ee9
27 changed files with 1251 additions and 88 deletions
|
@ -34,6 +34,7 @@ from .interface.part import PARTInterface, PARTInterfaceAnon, PARTInterfaceBlind
|
|||
from . import __version__
|
||||
from .rpc import escape_rpcauth
|
||||
from .rpc_xmr import make_xmr_rpc2_func
|
||||
from .rpc_wow import make_wow_rpc2_func
|
||||
from .ui.util import getCoinName, known_chart_coins
|
||||
from .util import (
|
||||
AutomationConstraint,
|
||||
|
@ -180,6 +181,21 @@ def threadPollXMRChainState(swap_client, coin_type):
|
|||
swap_client.chainstate_delay_event.wait(random.randrange(20, 30)) # Random to stagger updates
|
||||
|
||||
|
||||
def threadPollWOWChainState(swap_client, coin_type):
|
||||
ci = swap_client.ci(coin_type)
|
||||
cc = swap_client.coin_clients[coin_type]
|
||||
while not swap_client.chainstate_delay_event.is_set():
|
||||
try:
|
||||
new_height = ci.getChainHeight()
|
||||
if new_height != cc['chain_height']:
|
||||
swap_client.log.debug('New {} block at height: {}'.format(ci.ticker(), new_height))
|
||||
with swap_client.mxDB:
|
||||
cc['chain_height'] = new_height
|
||||
except Exception as e:
|
||||
swap_client.log.warning('threadPollWOWChainState {}, error: {}'.format(ci.ticker(), str(e)))
|
||||
swap_client.chainstate_delay_event.wait(random.randrange(20, 30)) # Random to stagger updates
|
||||
|
||||
|
||||
def threadPollChainState(swap_client, coin_type):
|
||||
ci = swap_client.ci(coin_type)
|
||||
cc = swap_client.coin_clients[coin_type]
|
||||
|
@ -281,7 +297,7 @@ class BasicSwap(BaseApp):
|
|||
|
||||
# TODO: Set dynamically
|
||||
self.balance_only_coins = (Coins.LTC_MWEB, )
|
||||
self.scriptless_coins = (Coins.XMR, Coins.PART_ANON, Coins.FIRO)
|
||||
self.scriptless_coins = (Coins.XMR, Coins.WOW, Coins.PART_ANON, Coins.FIRO)
|
||||
self.adaptor_swap_only_coins = self.scriptless_coins + (Coins.PART_BLIND, )
|
||||
self.coins_without_segwit = (Coins.PIVX, Coins.DASH, Coins.NMC)
|
||||
|
||||
|
@ -523,7 +539,7 @@ class BasicSwap(BaseApp):
|
|||
if self.coin_clients[coin]['connection_type'] == 'rpc':
|
||||
if coin == Coins.DCR:
|
||||
self.coin_clients[coin]['walletrpcport'] = chain_client_settings['walletrpcport']
|
||||
elif coin == Coins.XMR:
|
||||
elif coin in (Coins.XMR, Coins.WOW):
|
||||
self.coin_clients[coin]['rpctimeout'] = chain_client_settings.get('rpctimeout', 60)
|
||||
self.coin_clients[coin]['walletrpctimeout'] = chain_client_settings.get('walletrpctimeout', 120)
|
||||
self.coin_clients[coin]['walletrpctimeoutlong'] = chain_client_settings.get('walletrpctimeoutlong', 600)
|
||||
|
@ -561,9 +577,9 @@ class BasicSwap(BaseApp):
|
|||
if self.use_tor_proxy:
|
||||
have_cc_tor_opt = 'use_tor' in chain_client_settings
|
||||
if have_cc_tor_opt and chain_client_settings['use_tor'] is False:
|
||||
self.log.warning('use_tor is true for system but false for XMR.')
|
||||
self.log.warning('use_tor is true for system but false for ' + coin + '.')
|
||||
elif have_cc_tor_opt is False and is_private_ip_address(node_host):
|
||||
self.log.warning(f'Not using proxy for XMR node at private ip address {node_host}.')
|
||||
self.log.warning(f'Not using proxy for {coin} node at private ip address {node_host}.')
|
||||
else:
|
||||
proxy_host = self.tor_proxy_host
|
||||
proxy_port = self.tor_proxy_port
|
||||
|
@ -585,7 +601,10 @@ class BasicSwap(BaseApp):
|
|||
if proxy_host:
|
||||
self.log.info(f'Connecting through proxy at {proxy_host}.')
|
||||
|
||||
return make_xmr_rpc2_func(rpcport, daemon_login, rpchost, proxy_host=proxy_host, proxy_port=proxy_port)
|
||||
if coin == Coins.XMR:
|
||||
return make_xmr_rpc2_func(rpcport, daemon_login, rpchost, proxy_host=proxy_host, proxy_port=proxy_port)
|
||||
if coin == Coins.WOW:
|
||||
return make_wow_rpc2_func(rpcport, daemon_login, rpchost, proxy_host=proxy_host, proxy_port=proxy_port)
|
||||
|
||||
daemon_login = None
|
||||
if coin_settings.get('rpcuser', '') != '':
|
||||
|
@ -687,6 +706,12 @@ class BasicSwap(BaseApp):
|
|||
chain_client_settings = self.getChainClientSettings(coin)
|
||||
xmr_i.setWalletFilename(chain_client_settings['walletfile'])
|
||||
return xmr_i
|
||||
elif coin == Coins.WOW:
|
||||
from .interface.wow import WOWInterface
|
||||
wow_i = WOWInterface(self.coin_clients[coin], self.chain, self)
|
||||
chain_client_settings = self.getChainClientSettings(coin)
|
||||
wow_i.setWalletFilename(chain_client_settings['walletfile'])
|
||||
return wow_i
|
||||
elif coin == Coins.PIVX:
|
||||
from .interface.pivx import PIVXInterface
|
||||
return PIVXInterface(self.coin_clients[coin], self.chain, self)
|
||||
|
@ -711,7 +736,7 @@ class BasicSwap(BaseApp):
|
|||
|
||||
def setCoinRunParams(self, coin):
|
||||
cc = self.coin_clients[coin]
|
||||
if coin == Coins.XMR:
|
||||
if coin in (Coins.XMR, Coins.WOW):
|
||||
return
|
||||
if cc['connection_type'] == 'rpc' and cc['rpcauth'] is None:
|
||||
chain_client_settings = self.getChainClientSettings(coin)
|
||||
|
@ -782,8 +807,14 @@ class BasicSwap(BaseApp):
|
|||
core_version = ci.getDaemonVersion()
|
||||
self.log.info('%s Core version %d', ci.coin_name(), core_version)
|
||||
self.coin_clients[c]['core_version'] = core_version
|
||||
# thread_func = threadPollXMRChainState if c in (Coins.XMR, Coins.WOW) else threadPollChainState
|
||||
if c == Coins.XMR:
|
||||
thread_func = threadPollXMRChainState
|
||||
elif c == Coins.WOW:
|
||||
thread_func = threadPollWOWChainState
|
||||
else:
|
||||
thread_func = threadPollChainState
|
||||
|
||||
thread_func = threadPollXMRChainState if c == Coins.XMR else threadPollChainState
|
||||
t = threading.Thread(target=thread_func, args=(self, c))
|
||||
self.threads.append(t)
|
||||
t.start()
|
||||
|
@ -808,6 +839,12 @@ class BasicSwap(BaseApp):
|
|||
except Exception as e:
|
||||
self.log.warning('Can\'t open XMR wallet, could be locked.')
|
||||
continue
|
||||
elif c == Coins.WOW:
|
||||
try:
|
||||
ci.ensureWalletExists()
|
||||
except Exception as e:
|
||||
self.log.warning('Can\'t open WOW wallet, could be locked.')
|
||||
continue
|
||||
elif c == Coins.LTC:
|
||||
ci_mweb = self.ci(Coins.LTC_MWEB)
|
||||
is_encrypted, _ = self.getLockedState()
|
||||
|
@ -858,7 +895,7 @@ class BasicSwap(BaseApp):
|
|||
self.log.info('Scanned %d unread messages.', nm)
|
||||
|
||||
def stopDaemon(self, coin) -> None:
|
||||
if coin in (Coins.XMR, Coins.DCR):
|
||||
if coin in (Coins.XMR, Coins.DCR, Coins.WOW):
|
||||
return
|
||||
num_tries = 10
|
||||
authcookiepath = os.path.join(self.getChainDatadirPath(coin), '.cookie')
|
||||
|
@ -897,7 +934,7 @@ class BasicSwap(BaseApp):
|
|||
|
||||
if with_wallet:
|
||||
self.waitForDaemonRPC(coin_type, with_wallet=False)
|
||||
if coin_type in (Coins.XMR,):
|
||||
if coin_type in (Coins.XMR, Coins.WOW):
|
||||
return
|
||||
ci = self.ci(coin_type)
|
||||
# checkWallets can adjust the wallet name.
|
||||
|
@ -932,7 +969,7 @@ class BasicSwap(BaseApp):
|
|||
raise ValueError('{} has an unexpected wallet seed and "restrict_unknown_seed_wallets" is enabled.'.format(ci.coin_name()))
|
||||
if self.coin_clients[c]['connection_type'] != 'rpc':
|
||||
continue
|
||||
if c == Coins.XMR:
|
||||
if c in (Coins.XMR, Coins.WOW):
|
||||
continue # TODO
|
||||
synced = round(ci.getBlockchainInfo()['verificationprogress'], 3)
|
||||
if synced < 1.0:
|
||||
|
@ -1035,7 +1072,7 @@ class BasicSwap(BaseApp):
|
|||
db_key_coin_name = ci.coin_name().lower()
|
||||
self.log.info('Initialising {} wallet.'.format(ci.coin_name()))
|
||||
|
||||
if coin_type == Coins.XMR:
|
||||
if coin_type in (Coins.XMR, Coins.WOW):
|
||||
key_view = self.getWalletKey(coin_type, 1, for_ed25519=True)
|
||||
key_spend = self.getWalletKey(coin_type, 2, for_ed25519=True)
|
||||
ci.initialiseWallet(key_view, key_spend)
|
||||
|
@ -1951,7 +1988,7 @@ class BasicSwap(BaseApp):
|
|||
return self.ci(coin_type).get_fee_rate(conf_target)
|
||||
|
||||
def estimateWithdrawFee(self, coin_type, fee_rate):
|
||||
if coin_type == Coins.XMR:
|
||||
if coin_type in (Coins.XMR, Coins.WOW):
|
||||
# Fee estimate must be manually initiated
|
||||
return None
|
||||
tx_vsize = self.ci(coin_type).getHTLCSpendTxVSize()
|
||||
|
@ -1960,7 +1997,7 @@ class BasicSwap(BaseApp):
|
|||
|
||||
def withdrawCoin(self, coin_type, value, addr_to, subfee: bool) -> str:
|
||||
ci = self.ci(coin_type)
|
||||
if subfee and coin_type == Coins.XMR:
|
||||
if subfee and coin_type in (Coins.XMR, Coins.WOW):
|
||||
self.log.info('withdrawCoin sweep all {} to {}'.format(ci.ticker(), addr_to))
|
||||
else:
|
||||
self.log.info('withdrawCoin {} {} to {} {}'.format(value, ci.ticker(), addr_to, ' subfee' if subfee else ''))
|
||||
|
@ -2012,7 +2049,7 @@ class BasicSwap(BaseApp):
|
|||
if c == Coins.PART:
|
||||
ci.setWalletSeedWarning(False) # All keys should be be derived from the Particl mnemonic
|
||||
return True # TODO
|
||||
if c == Coins.XMR:
|
||||
if c in (Coins.XMR, Coins.WOW):
|
||||
expect_address = self.getCachedMainWalletAddress(ci)
|
||||
if expect_address is None:
|
||||
self.log.warning('Can\'t find expected main wallet address for coin {}'.format(ci.coin_name()))
|
||||
|
@ -2056,7 +2093,7 @@ class BasicSwap(BaseApp):
|
|||
# TODO: How to scan pruned blocks?
|
||||
|
||||
if not self.checkWalletSeed(coin_type):
|
||||
if coin_type == Coins.XMR:
|
||||
if coin_type in (Coins.XMR, Coins.WOW):
|
||||
raise ValueError('TODO: How to reseed XMR wallet?')
|
||||
else:
|
||||
raise ValueError('Wallet seed doesn\'t match expected.')
|
||||
|
@ -5865,7 +5902,7 @@ class BasicSwap(BaseApp):
|
|||
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, KeyTypes.KBSL, for_ed25519)
|
||||
vkbs = ci_to.sumKeys(kbsl, kbsf)
|
||||
|
||||
if coin_to == Coins.XMR:
|
||||
if coin_to == (Coins.XMR, Coins.WOW):
|
||||
address_to = self.getCachedMainWalletAddress(ci_to, session)
|
||||
elif coin_to in (Coins.PART_BLIND, Coins.PART_ANON):
|
||||
address_to = self.getCachedStealthAddressForCoin(coin_to, session)
|
||||
|
@ -5932,7 +5969,7 @@ class BasicSwap(BaseApp):
|
|||
vkbs = ci_to.sumKeys(kbsl, kbsf)
|
||||
|
||||
try:
|
||||
if offer.coin_to == Coins.XMR:
|
||||
if offer.coin_to in (Coins.XMR, Coins.WOW):
|
||||
address_to = self.getCachedMainWalletAddress(ci_to, session)
|
||||
elif coin_to in (Coins.PART_BLIND, Coins.PART_ANON):
|
||||
address_to = self.getCachedStealthAddressForCoin(coin_to, session)
|
||||
|
@ -6984,7 +7021,7 @@ class BasicSwap(BaseApp):
|
|||
rv['anon_pending'] = walletinfo['unconfirmed_anon'] + walletinfo['immature_anon_balance']
|
||||
rv['blind_balance'] = walletinfo['blind_balance']
|
||||
rv['blind_unconfirmed'] = walletinfo['unconfirmed_blind']
|
||||
elif coin == Coins.XMR:
|
||||
elif coin in (Coins.XMR, Coins.WOW):
|
||||
rv['main_address'] = self.getCachedMainWalletAddress(ci)
|
||||
elif coin == Coins.NAV:
|
||||
rv['immature'] = walletinfo['immature_balance']
|
||||
|
|
|
@ -10,6 +10,7 @@ from .util import (
|
|||
)
|
||||
|
||||
XMR_COIN = 10 ** 12
|
||||
WOW_COIN = 10 ** 11
|
||||
|
||||
|
||||
class Coins(IntEnum):
|
||||
|
@ -21,13 +22,14 @@ class Coins(IntEnum):
|
|||
XMR = 6
|
||||
PART_BLIND = 7
|
||||
PART_ANON = 8
|
||||
# ZANO = 9
|
||||
WOW = 9
|
||||
# NDAU = 10
|
||||
PIVX = 11
|
||||
DASH = 12
|
||||
FIRO = 13
|
||||
NAV = 14
|
||||
LTC_MWEB = 15
|
||||
# ZANO = 16 was 9
|
||||
|
||||
|
||||
chainparams = {
|
||||
|
@ -247,6 +249,33 @@ chainparams = {
|
|||
'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,
|
||||
},
|
||||
'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,
|
||||
}
|
||||
},
|
||||
Coins.PIVX: {
|
||||
'name': 'pivx',
|
||||
'ticker': 'PIVX',
|
||||
|
|
|
@ -36,3 +36,7 @@ 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)
|
||||
|
||||
WOW_BINDIR = os.path.expanduser(os.getenv('WOW_BINDIR', os.path.join(DEFAULT_TEST_BINDIR, 'wownero')))
|
||||
WOWD = os.getenv('WOWD', 'wownerod' + bin_suffix)
|
||||
WOW_WALLET_RPC = os.getenv('WOW_WALLET_RPC', 'wownero-wallet-rpc' + bin_suffix)
|
||||
|
|
|
@ -311,6 +311,22 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
else:
|
||||
raise ValueError('Unknown RPC variant')
|
||||
result = json.dumps(rv, indent=4)
|
||||
elif coin_type == Coins.WOW:
|
||||
ci = swap_client.ci(coin_type)
|
||||
arr = cmd.split(None, 1)
|
||||
method = arr[0]
|
||||
params = json.loads(arr[1]) if len(arr) > 1 else []
|
||||
if coin_id == -8:
|
||||
rv = ci.rpc_wallet(method, params)
|
||||
elif coin_id == -7:
|
||||
rv = ci.rpc(method, params)
|
||||
elif coin_id == -6:
|
||||
if params == []:
|
||||
params = None
|
||||
rv = ci.rpc2(method, params)
|
||||
else:
|
||||
raise ValueError('Unknown WOW RPC variant')
|
||||
result = json.dumps(rv, indent=4)
|
||||
else:
|
||||
if call_type == 'http':
|
||||
ci = swap_client.ci(coin_type)
|
||||
|
@ -338,6 +354,7 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
|
||||
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, )]
|
||||
|
||||
if any(c[0] == Coins.DCR for c in coin_available):
|
||||
|
@ -348,6 +365,10 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
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'))
|
||||
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
|
|
577
basicswap/interface/wow.py
Normal file
577
basicswap/interface/wow.py
Normal file
|
@ -0,0 +1,577 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020-2024 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
import basicswap.contrib.ed25519_fast as edf
|
||||
import basicswap.ed25519_fast_util as edu
|
||||
import basicswap.util_xmr as wow_util
|
||||
from coincurve.ed25519 import (
|
||||
ed25519_add,
|
||||
ed25519_get_pubkey,
|
||||
ed25519_scalar_add,
|
||||
)
|
||||
from coincurve.keys import PrivateKey
|
||||
from coincurve.dleag import (
|
||||
dleag_prove,
|
||||
dleag_verify,
|
||||
dleag_proof_len,
|
||||
verify_ed25519_point,
|
||||
)
|
||||
|
||||
from basicswap.interface import (
|
||||
Curves)
|
||||
from basicswap.util import (
|
||||
i2b, b2i, b2h,
|
||||
dumpj,
|
||||
ensure,
|
||||
make_int,
|
||||
TemporaryError)
|
||||
from basicswap.util.network import (
|
||||
is_private_ip_address)
|
||||
from basicswap.rpc_wow import (
|
||||
make_wow_rpc_func,
|
||||
make_wow_rpc2_func)
|
||||
from basicswap.chainparams import WOW_COIN, CoinInterface, Coins
|
||||
|
||||
|
||||
class WOWInterface(CoinInterface):
|
||||
@staticmethod
|
||||
def curve_type():
|
||||
return Curves.ed25519
|
||||
|
||||
@staticmethod
|
||||
def coin_type():
|
||||
return Coins.WOW
|
||||
|
||||
@staticmethod
|
||||
def COIN():
|
||||
return WOW_COIN
|
||||
|
||||
@staticmethod
|
||||
def exp() -> int:
|
||||
return 11
|
||||
|
||||
@staticmethod
|
||||
def nbk() -> int:
|
||||
return 32
|
||||
|
||||
@staticmethod
|
||||
def nbK() -> int: # No. of bytes requires to encode a public key
|
||||
return 32
|
||||
|
||||
@staticmethod
|
||||
def depth_spendable() -> int:
|
||||
return 10
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||
raise ValueError('Not possible')
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_b_lock_spend_tx_vsize() -> int:
|
||||
# TODO: Estimate with ringsize
|
||||
return 1604
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super().__init__(network)
|
||||
|
||||
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._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', ''))
|
||||
|
||||
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']
|
||||
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 WOW)'
|
||||
elif have_cc_tor_opt is False and is_private_ip_address(rpchost):
|
||||
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}.')
|
||||
else:
|
||||
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._rpctimeout = coin_settings.get('rpctimeout', 60)
|
||||
self._walletrpctimeout = coin_settings.get('walletrpctimeout', 120)
|
||||
self._walletrpctimeoutlong = coin_settings.get('walletrpctimeoutlong', 600)
|
||||
|
||||
self.rpc = make_wow_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_wow_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_wow_rpc_func(coin_settings['walletrpcport'], coin_settings['walletrpcauth'], host=coin_settings.get('walletrpchost', '127.0.0.1'), default_timeout=self._walletrpctimeout, tag='Wallet ')
|
||||
|
||||
def checkWallets(self) -> int:
|
||||
return 1
|
||||
|
||||
def setFeePriority(self, new_priority):
|
||||
ensure(new_priority >= 0 and new_priority < 4, 'Invalid fee_priority value')
|
||||
self._fee_priority = new_priority
|
||||
|
||||
def setWalletFilename(self, wallet_filename):
|
||||
self._wallet_filename = wallet_filename
|
||||
|
||||
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))
|
||||
|
||||
def openWallet(self, filename):
|
||||
params = {'filename': filename}
|
||||
if self._wallet_password is not None:
|
||||
params['password'] = self._wallet_password
|
||||
|
||||
try:
|
||||
# Can't reopen the same wallet in windows, !is_keys_file_locked()
|
||||
self.rpc_wallet('close_wallet')
|
||||
except Exception:
|
||||
pass
|
||||
self.rpc_wallet('open_wallet', params)
|
||||
|
||||
def initialiseWallet(self, key_view, key_spend, restore_height=None):
|
||||
with self._mx_wallet:
|
||||
try:
|
||||
self.openWallet(self._wallet_filename)
|
||||
# TODO: Check address
|
||||
return # Wallet exists
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
Kbv = self.getPubkey(key_view)
|
||||
Kbs = self.getPubkey(key_spend)
|
||||
address_b58 = wow_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,
|
||||
}
|
||||
self.createWallet(params)
|
||||
self.openWallet(self._wallet_filename)
|
||||
|
||||
def ensureWalletExists(self) -> None:
|
||||
with self._mx_wallet:
|
||||
self.openWallet(self._wallet_filename)
|
||||
|
||||
def testDaemonRPC(self, with_wallet=True) -> None:
|
||||
self.rpc_wallet('get_languages')
|
||||
|
||||
def getDaemonVersion(self):
|
||||
return self.rpc_wallet('get_version')['version']
|
||||
|
||||
def getBlockchainInfo(self):
|
||||
get_height = self.rpc2('get_height', timeout=self._rpctimeout)
|
||||
rv = {
|
||||
'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']
|
||||
|
||||
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']
|
||||
except Exception as e:
|
||||
self._log.warning('WOW get_block_count failed with: %s', str(e))
|
||||
rv['verificationprogress'] = 0.0
|
||||
|
||||
return rv
|
||||
|
||||
def getChainHeight(self):
|
||||
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}
|
||||
return rv
|
||||
raise e
|
||||
|
||||
rv = {}
|
||||
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
|
||||
return rv
|
||||
|
||||
def walletRestoreHeight(self):
|
||||
return self._restore_height
|
||||
|
||||
def getMainWalletAddress(self) -> str:
|
||||
with self._mx_wallet:
|
||||
self.openWallet(self._wallet_filename)
|
||||
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')
|
||||
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')
|
||||
if conf_target <= 1:
|
||||
conf_target = 1 # normal
|
||||
else:
|
||||
conf_target = 0 # slow
|
||||
fee_per_k_bytes = fee_est['fees'][conf_target] * 1000
|
||||
|
||||
return float(self.format_amount(fee_per_k_bytes)), 'get_fee_estimate'
|
||||
|
||||
def getNewSecretKey(self) -> bytes:
|
||||
# Note: Returned bytes are in big endian order
|
||||
return i2b(edu.get_secret())
|
||||
|
||||
def pubkey(self, key: bytes) -> bytes:
|
||||
return edf.scalarmult_B(key)
|
||||
|
||||
def encodeKey(self, vk: bytes) -> str:
|
||||
return vk[::-1].hex()
|
||||
|
||||
def decodeKey(self, k_hex: str) -> bytes:
|
||||
return bytes.fromhex(k_hex)[::-1]
|
||||
|
||||
def encodePubkey(self, pk: bytes) -> str:
|
||||
return edu.encodepoint(pk)
|
||||
|
||||
def decodePubkey(self, pke):
|
||||
return edf.decodepoint(pke)
|
||||
|
||||
def getPubkey(self, privkey):
|
||||
return ed25519_get_pubkey(privkey)
|
||||
|
||||
def getAddressFromKeys(self, key_view: bytes, key_spend: bytes) -> str:
|
||||
pk_view = self.getPubkey(key_view)
|
||||
pk_spend = self.getPubkey(key_spend)
|
||||
return wow_util.encode_address(pk_view, pk_spend, self._addr_prefix)
|
||||
|
||||
def verifyKey(self, k: int) -> bool:
|
||||
i = b2i(k)
|
||||
return (i < edf.l and i > 8)
|
||||
|
||||
def verifyPubkey(self, pubkey_bytes):
|
||||
# Calls ed25519_decode_check_point() in secp256k1
|
||||
# Checks for small order
|
||||
return verify_ed25519_point(pubkey_bytes)
|
||||
|
||||
def proveDLEAG(self, key: bytes) -> bytes:
|
||||
privkey = PrivateKey(key)
|
||||
return dleag_prove(privkey)
|
||||
|
||||
def verifyDLEAG(self, dleag_bytes: bytes) -> bool:
|
||||
return dleag_verify(dleag_bytes)
|
||||
|
||||
def lengthDLEAG(self) -> int:
|
||||
return dleag_proof_len()
|
||||
|
||||
def sumKeys(self, ka: bytes, kb: bytes) -> bytes:
|
||||
return ed25519_scalar_add(ka, kb)
|
||||
|
||||
def sumPubkeys(self, Ka: bytes, Kb: bytes) -> bytes:
|
||||
return ed25519_add(Ka, Kb)
|
||||
|
||||
def encodeSharedAddress(self, Kbv: bytes, Kbs: bytes) -> str:
|
||||
return wow_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:
|
||||
with self._mx_wallet:
|
||||
self.openWallet(self._wallet_filename)
|
||||
self.rpc_wallet('refresh')
|
||||
|
||||
Kbv = self.getPubkey(kbv)
|
||||
shared_addr = wow_util.encode_address(Kbv, Kbs, self._addr_prefix)
|
||||
|
||||
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'])
|
||||
|
||||
return tx_hash
|
||||
|
||||
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 = wow_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),
|
||||
}
|
||||
|
||||
try:
|
||||
self.openWallet(address_b58)
|
||||
except Exception as e:
|
||||
self.createWallet(params)
|
||||
self.openWallet(address_b58)
|
||||
|
||||
self.rpc_wallet('refresh', timeout=self._walletrpctimeoutlong)
|
||||
|
||||
'''
|
||||
# Debug
|
||||
try:
|
||||
current_height = self.rpc_wallet('get_height')['height']
|
||||
self._log.info('findTxB WOW current_height %d\nAddress: %s', current_height, address_b58)
|
||||
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
|
||||
# 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)
|
||||
rv = None
|
||||
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 unlock_time != 0:
|
||||
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']}
|
||||
else:
|
||||
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)
|
||||
|
||||
try:
|
||||
current_height = self.rpc2('get_height', timeout=self._rpctimeout)['height']
|
||||
self._log.info('findTxnByHash WOW current_height %d\nhash: %s', current_height, 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
|
||||
|
||||
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) -> 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 = wow_util.encode_address(Kbv, Kbs, self._addr_prefix)
|
||||
|
||||
wallet_filename = address_b58 + '_spend'
|
||||
|
||||
params = {
|
||||
'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:
|
||||
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']
|
||||
if len(txns) > 0:
|
||||
txid = txns[0]['txid']
|
||||
self._log.warning(f'spendBLockTx detected spending tx: {txid}.')
|
||||
if txns[0]['address'] == address_b58:
|
||||
return bytes.fromhex(txid)
|
||||
|
||||
self._log.error('wallet {} balance {}, expected {}'.format(wallet_filename, rv['balance'], cb_swap_value))
|
||||
|
||||
if not spend_actual_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')
|
||||
|
||||
params = {'address': address_to}
|
||||
if self._fee_priority > 0:
|
||||
params['priority'] = self._fee_priority
|
||||
|
||||
rv = self.rpc_wallet('sweep_all', params)
|
||||
self._log.debug('sweep_all {}'.format(json.dumps(rv)))
|
||||
|
||||
return bytes.fromhex(rv['tx_hash_list'][0])
|
||||
|
||||
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')
|
||||
|
||||
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('WOW {} sweep_all.'.format('estimate fee' if estimate_fee else 'withdraw'))
|
||||
self._log.debug('WOW balance: {}'.format(balance['balance']))
|
||||
params = {'address': addr_to, 'do_not_relay': estimate_fee}
|
||||
if self._fee_priority > 0:
|
||||
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]
|
||||
|
||||
value_sats: int = make_int(value, self.exp())
|
||||
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)
|
||||
if estimate_fee:
|
||||
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)
|
||||
|
||||
def showLockTransfers(self, kbv, Kbs, restore_height):
|
||||
with self._mx_wallet:
|
||||
try:
|
||||
Kbv = self.getPubkey(kbv)
|
||||
address_b58 = wow_util.encode_address(Kbv, Kbs, self._addr_prefix)
|
||||
wallet_file = address_b58 + '_spend'
|
||||
try:
|
||||
self.openWallet(wallet_file)
|
||||
except Exception:
|
||||
wallet_file = address_b58
|
||||
try:
|
||||
self.openWallet(wallet_file)
|
||||
except Exception:
|
||||
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),
|
||||
}
|
||||
self.createWallet(params)
|
||||
self.openWallet(address_b58)
|
||||
|
||||
self.rpc_wallet('refresh')
|
||||
|
||||
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)}
|
||||
|
||||
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']
|
||||
|
||||
def changeWalletPassword(self, old_password, new_password):
|
||||
self._log.info('changeWalletPassword - {}'.format(self.ticker()))
|
||||
orig_password = self._wallet_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})
|
||||
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._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._wallet_password = None
|
||||
|
||||
def isAddressMine(self, address):
|
||||
# TODO
|
||||
return True
|
||||
|
||||
def ensureFunds(self, amount: int) -> None:
|
||||
if self.getSpendableBalance() < amount:
|
||||
raise ValueError('Balance too low')
|
||||
|
||||
def getTransaction(self, txid: bytes):
|
||||
return self.rpc2('get_transactions', {'txs_hashes': [txid.hex(), ]})
|
|
@ -54,7 +54,7 @@ def withdraw_coin(swap_client, coin_type, post_string, is_json):
|
|||
post_data = getFormData(post_string, is_json)
|
||||
address = get_data_entry(post_data, 'address')
|
||||
|
||||
if coin_type == Coins.XMR:
|
||||
if coin_type in (Coins.XMR, Coins.WOW):
|
||||
value = None
|
||||
sweepall = get_data_entry(post_data, 'sweepall')
|
||||
if not isinstance(sweepall, bool):
|
||||
|
@ -74,7 +74,7 @@ def withdraw_coin(swap_client, coin_type, post_string, is_json):
|
|||
elif coin_type == Coins.LTC:
|
||||
type_from = get_data_entry_or(post_data, 'type_from', 'plain')
|
||||
txid_hex = swap_client.withdrawLTC(type_from, value, address, subfee)
|
||||
elif coin_type == Coins.XMR:
|
||||
elif coin_type in (Coins.XMR, Coins.WOW):
|
||||
txid_hex = swap_client.withdrawCoin(coin_type, value, address, sweepall)
|
||||
else:
|
||||
txid_hex = swap_client.withdrawCoin(coin_type, value, address, subfee)
|
||||
|
@ -685,7 +685,7 @@ def js_getcoinseed(self, url_split, post_string, is_json) -> bytes:
|
|||
raise ValueError('Particl wallet seed is set from the Basicswap mnemonic.')
|
||||
|
||||
ci = swap_client.ci(coin)
|
||||
if coin == Coins.XMR:
|
||||
if coin in (Coins.XMR, Coins.WOW):
|
||||
key_view = swap_client.getWalletKey(coin, 1, for_ed25519=True)
|
||||
key_spend = swap_client.getWalletKey(coin, 2, for_ed25519=True)
|
||||
address = ci.getAddressFromKeys(key_view, key_spend)
|
||||
|
|
258
basicswap/rpc_wow.py
Normal file
258
basicswap/rpc_wow.py
Normal file
|
@ -0,0 +1,258 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import json
|
||||
import socks
|
||||
import time
|
||||
import urllib
|
||||
import hashlib
|
||||
from xmlrpc.client import (
|
||||
Fault,
|
||||
Transport,
|
||||
SafeTransport,
|
||||
)
|
||||
from sockshandler import SocksiPyConnection
|
||||
from .util import jsonDecimal
|
||||
|
||||
|
||||
class SocksTransport(Transport):
|
||||
|
||||
def set_proxy(self, proxy_host, proxy_port):
|
||||
self.proxy_host = proxy_host
|
||||
self.proxy_port = proxy_port
|
||||
|
||||
self.proxy_type = socks.PROXY_TYPE_SOCKS5
|
||||
self.proxy_rdns = True
|
||||
self.proxy_username = None
|
||||
self.proxy_password = None
|
||||
|
||||
def make_connection(self, host):
|
||||
# return an existing connection if possible. This allows
|
||||
# HTTP/1.1 keep-alive.
|
||||
if self._connection and host == self._connection[0]:
|
||||
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)
|
||||
return self._connection[1]
|
||||
|
||||
|
||||
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):
|
||||
|
||||
parsed = urllib.parse.urlparse(uri)
|
||||
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
|
||||
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.__verbose = verbose
|
||||
self.__allow_none = allow_none
|
||||
|
||||
self.__request_id = 0
|
||||
|
||||
def close(self):
|
||||
if self.__transport is not None:
|
||||
self.__transport.close()
|
||||
|
||||
def request_id(self):
|
||||
return self.__request_id
|
||||
|
||||
def post_request(self, method, params, timeout=None):
|
||||
try:
|
||||
connection = self.__transport.make_connection(self.__host)
|
||||
if timeout:
|
||||
connection.timeout = timeout
|
||||
headers = self.__transport._extra_headers[:]
|
||||
|
||||
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.__request_id += 1
|
||||
|
||||
resp = connection.getresponse()
|
||||
return resp.read()
|
||||
|
||||
except Fault:
|
||||
raise
|
||||
except Exception:
|
||||
self.__transport.close()
|
||||
raise
|
||||
|
||||
def json_request(self, request_body, username='', password='', timeout=None):
|
||||
try:
|
||||
connection = self.__transport.make_connection(self.__host)
|
||||
if timeout:
|
||||
connection.timeout = timeout
|
||||
|
||||
headers = self.__transport._extra_headers[:]
|
||||
|
||||
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 '')
|
||||
resp = connection.getresponse()
|
||||
|
||||
if resp.status == 401:
|
||||
resp_headers = resp.getheaders()
|
||||
v = resp.read()
|
||||
|
||||
algorithm = ''
|
||||
realm = ''
|
||||
nonce = ''
|
||||
for h in resp_headers:
|
||||
if h[0] != 'WWW-authenticate':
|
||||
continue
|
||||
fields = h[1].split(',')
|
||||
for f in fields:
|
||||
key, value = f.split('=', 1)
|
||||
if key == 'algorithm' and value != 'MD5':
|
||||
break
|
||||
if key == 'realm':
|
||||
realm = value.strip('"')
|
||||
if key == 'nonce':
|
||||
nonce = value.strip('"')
|
||||
if realm != '' and nonce != '':
|
||||
break
|
||||
|
||||
if realm == '' or nonce == '':
|
||||
raise ValueError('Authenticate header not found.')
|
||||
|
||||
path = self.__handler
|
||||
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()
|
||||
|
||||
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])
|
||||
|
||||
# MD5-SESS
|
||||
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()
|
||||
|
||||
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))
|
||||
|
||||
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 '')
|
||||
resp = connection.getresponse()
|
||||
|
||||
self.__request_id += 1
|
||||
return resp.read()
|
||||
|
||||
except Fault:
|
||||
raise
|
||||
except Exception:
|
||||
self.__transport.close()
|
||||
raise
|
||||
|
||||
|
||||
def callrpc_wow(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)
|
||||
else:
|
||||
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()
|
||||
}
|
||||
if auth:
|
||||
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'))
|
||||
except Exception as 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']))
|
||||
|
||||
return r['result']
|
||||
|
||||
|
||||
def callrpc_wow2(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)
|
||||
else:
|
||||
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)
|
||||
else:
|
||||
v = x.json_request(params, timeout=timeout)
|
||||
x.close()
|
||||
r = json.loads(v.decode('utf-8'))
|
||||
except Exception as ex:
|
||||
raise ValueError('{}RPC Server Error: {}'.format(tag, str(ex)))
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def make_wow_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
|
||||
transport = None
|
||||
default_timeout = default_timeout
|
||||
tag = tag
|
||||
|
||||
if proxy_host:
|
||||
transport = SocksTransport()
|
||||
transport.set_proxy(proxy_host, proxy_port)
|
||||
|
||||
def rpc_func(method, params=None, wallet=None, timeout=default_timeout):
|
||||
nonlocal port, auth, host, transport, tag
|
||||
return callrpc_wow2(port, method, params, auth=auth, rpc_host=host, timeout=timeout, transport=transport, tag=tag)
|
||||
return rpc_func
|
||||
|
||||
|
||||
def make_wow_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
|
||||
transport = None
|
||||
default_timeout = default_timeout
|
||||
tag = tag
|
||||
|
||||
if proxy_host:
|
||||
transport = SocksTransport()
|
||||
transport.set_proxy(proxy_host, proxy_port)
|
||||
|
||||
def rpc_func(method, params=None, wallet=None, timeout=default_timeout):
|
||||
nonlocal port, auth, host, transport, tag
|
||||
return callrpc_wow(port, method, params, rpc_host=host, auth=auth, timeout=timeout, transport=transport, tag=tag)
|
||||
return rpc_func
|
|
@ -409,7 +409,7 @@
|
|||
document.getElementById('get_rate_inferred_button').addEventListener('click', getRateInferred);
|
||||
|
||||
function set_swap_type_enabled(coin_from, coin_to, swap_type) {
|
||||
const adaptor_sig_only_coins = ['6' /* XMR */, '8' /* PART_ANON */, '7' /* PART_BLIND */, '13' /* FIRO */];
|
||||
const adaptor_sig_only_coins = ['6' /* XMR */,'9' /* WOW */, '8' /* PART_ANON */, '7' /* PART_BLIND */, '13' /* FIRO */];
|
||||
const secret_hash_only_coins = ['11' /* PIVX */, '12' /* DASH */];
|
||||
let make_hidden = false;
|
||||
if (adaptor_sig_only_coins.includes(coin_from) || adaptor_sig_only_coins.includes(coin_to)) {
|
||||
|
|
|
@ -105,7 +105,7 @@
|
|||
</tr>
|
||||
{% endif %}
|
||||
{% if c.manage_daemon is defined %}
|
||||
{% if c.name == 'monero' %}
|
||||
{% if c.name in ('wownero', 'monero') %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Manage Daemon</td>
|
||||
<td class="py-3 px-6">
|
||||
|
@ -175,7 +175,7 @@
|
|||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% if c.name == 'monero' %}
|
||||
{% if c.name in ('wownero', 'monero') %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Transaction Fee Priority</td>
|
||||
<td class="py-3 px-6">
|
||||
|
|
|
@ -266,8 +266,8 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
{# / PART #}
|
||||
{% if w.cid == '6' %}
|
||||
{# XMR #}
|
||||
{% if w.cid in '6, 9' %}
|
||||
{# XMR | WOW #}
|
||||
<div class="w-full md:w-1/2 p-3 flex justify-center items-center">
|
||||
<div class="h-full">
|
||||
<div class="flex flex-wrap -m-3">
|
||||
|
@ -412,8 +412,8 @@
|
|||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% if w.cid == '6' %}
|
||||
{# XMR #}
|
||||
{% if w.cid in '6, 9' %}
|
||||
{# XMR | WOW #}
|
||||
<script>
|
||||
// Monero Sub
|
||||
var moneroSubAddress = "{{ w.deposit_address }}";
|
||||
|
@ -638,8 +638,7 @@
|
|||
|
||||
floatBalance = parseFloat(balance);
|
||||
calculatedAmount = floatBalance * percent;
|
||||
|
||||
if (cid === '6' && percent === 1) {
|
||||
if (cid in '6, 9' && percent === 1) {
|
||||
amountInput.setAttribute('data-hidden', 'true');
|
||||
amountInput.placeholder = 'Sweep All';
|
||||
amountInput.value = '';
|
||||
|
@ -654,8 +653,7 @@
|
|||
amountInput.placeholder = '';
|
||||
amountInput.disabled = false;
|
||||
}
|
||||
|
||||
if (cid === '6' && percent === 1) {
|
||||
if (cid in '6, 9' && percent === 1) {
|
||||
var sweepAllCheckbox = document.getElementById('sweepall');
|
||||
if (sweepAllCheckbox) {
|
||||
sweepAllCheckbox.checked = true;
|
||||
|
@ -674,7 +672,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
{% if w.cid == '6' %} {# XMR #}
|
||||
{% if w.cid in '6, 9' %} {# XMR | WOW #}
|
||||
<td class="py-3 px-6 bold">Sweep All:</td>
|
||||
<td class="py-3 px-6"> <input class="hover:border-blue-500 w-5 h-5 form-check-input text-blue-600 bg-gray-50 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-1 dark:bg-gray-500 dark:border-gray-400" type="checkbox" id="sweepall" name="sweepall_{{ w.cid }}" {% if w.wd_sweepall==true %} checked=checked{% endif %}> </td> {% else %} <td class="py-3 px-6 bold">Subtract Fee:</td>
|
||||
<td class="py-3 px-6"> <input class="hover:border-blue-500 w-5 h-5 form-check-input text-blue-600 bg-gray-50 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-1 dark:bg-gray-500 dark:border-gray-400" type="checkbox" name="subfee_{{ w.cid }}" {% if w.wd_subfee==true %} checked=checked{% endif %}> </td>
|
||||
|
@ -753,17 +751,17 @@
|
|||
<div class="pt-6 pb-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl">
|
||||
<div class="px-6">
|
||||
<div class="flex flex-wrap justify-end">
|
||||
{% if w.cid != '6' %}
|
||||
{# !XMR #}
|
||||
{% if w.cid not in '6, 9' %}
|
||||
{# !XMR | WOW #}
|
||||
{% if w.show_utxo_groups %}
|
||||
{% else %}
|
||||
<div class="w-full md:w-auto p-1.5"> <button type="submit" class="flex flex-wrap justify-center px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="showutxogroups" name="showutxogroups" value="Show UTXO Groups"> {{ utxo_groups_svg | safe }} Show UTXO Groups </button> </div>
|
||||
{% endif %} {% endif %}
|
||||
{% if w.cid == '6' %}
|
||||
{# XMR #}
|
||||
{% if w.cid in '6, 9' %}
|
||||
{# XMR | WOW #}
|
||||
<div class="w-full md:w-auto p-1.5 ml-2"> <button type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" name="estfee_{{ w.cid }}" value="Estimate Fee">Estimate {{ w.ticker }} Fee </button> </div>
|
||||
{% endif %}
|
||||
{# / XMR #} <div class="w-full md:w-auto p-1.5 ml-2"> <button type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" name="withdraw_{{ w.cid }}" value="Withdraw" onclick="return confirmWithdrawal();">{{ withdraw_svg | safe }} Withdraw {{ w.ticker }} </div>
|
||||
{# / XMR | WOW #} <div class="w-full md:w-auto p-1.5 ml-2"> <button type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" name="withdraw_{{ w.cid }}" value="Withdraw" onclick="return confirmWithdrawal();">{{ withdraw_svg | safe }} Withdraw {{ w.ticker }} </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -772,8 +770,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% if w.cid != '6' %}
|
||||
{# !XMR #}
|
||||
{% if w.cid not in '6, 9' %}
|
||||
{# !XMR | WOW #}
|
||||
{% if w.show_utxo_groups %}
|
||||
<section class="p-6">
|
||||
<div class="flex items-center">
|
||||
|
@ -979,4 +977,4 @@
|
|||
</script>
|
||||
{% include 'footer.html' %}
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -213,11 +213,11 @@ const coinNameToSymbol = {
|
|||
'Particl Blind': 'PART',
|
||||
'Particl Anon': 'PART',
|
||||
'Monero': 'XMR',
|
||||
'Wownero': 'WOW',
|
||||
'Litecoin': 'LTC',
|
||||
'Firo': 'FIRO',
|
||||
'Dash': 'DASH',
|
||||
'PIVX': 'PIVX',
|
||||
'Wownero': 'WOW',
|
||||
'Decred': 'DCR',
|
||||
'Zano': 'ZANO',
|
||||
};
|
||||
|
|
|
@ -89,7 +89,7 @@ def parseOfferFormData(swap_client, form_data, page_data, options={}):
|
|||
page_data['coin_from'] = getCoinType(get_data_entry(form_data, 'coin_from'))
|
||||
coin_from = Coins(page_data['coin_from'])
|
||||
ci_from = swap_client.ci(coin_from)
|
||||
if coin_from != Coins.XMR:
|
||||
if coin_from not in (Coins.XMR, Coins.WOW):
|
||||
page_data['fee_from_conf'] = ci_from._conf_target # Set default value
|
||||
parsed_data['coin_from'] = coin_from
|
||||
except Exception:
|
||||
|
@ -99,7 +99,7 @@ def parseOfferFormData(swap_client, form_data, page_data, options={}):
|
|||
page_data['coin_to'] = getCoinType(get_data_entry(form_data, 'coin_to'))
|
||||
coin_to = Coins(page_data['coin_to'])
|
||||
ci_to = swap_client.ci(coin_to)
|
||||
if coin_to != Coins.XMR:
|
||||
if coin_to not in (Coins.XMR, Coins.WOW):
|
||||
page_data['fee_to_conf'] = ci_to._conf_target # Set default value
|
||||
parsed_data['coin_to'] = coin_to
|
||||
except Exception:
|
||||
|
@ -161,7 +161,7 @@ def parseOfferFormData(swap_client, form_data, page_data, options={}):
|
|||
page_data['swap_type'] = get_data_entry(form_data, 'swap_type')
|
||||
parsed_data['swap_type'] = page_data['swap_type']
|
||||
swap_type = swap_type_from_string(parsed_data['swap_type'])
|
||||
elif parsed_data['coin_to'] in (Coins.XMR, Coins.PART_ANON):
|
||||
elif parsed_data['coin_to'] in (Coins.XMR, Coins.WOW, Coins.PART_ANON):
|
||||
parsed_data['swap_type'] = strSwapType(SwapTypes.XMR_SWAP)
|
||||
swap_type = SwapTypes.XMR_SWAP
|
||||
else:
|
||||
|
@ -243,7 +243,7 @@ def parseOfferFormData(swap_client, form_data, page_data, options={}):
|
|||
page_data['amt_from_lock_spend_tx_fee'] = ci_from.format_amount(lock_spend_tx_fee // ci_from.COIN())
|
||||
page_data['tla_from'] = ci_from.ticker()
|
||||
|
||||
if ci_to == Coins.XMR:
|
||||
if ci_to in (Coins.XMR, Coins.WOW):
|
||||
if have_data_entry(form_data, 'fee_rate_to'):
|
||||
page_data['to_fee_override'] = get_data_entry(form_data, 'fee_rate_to')
|
||||
parsed_data['to_fee_override'] = page_data['to_fee_override']
|
||||
|
@ -267,7 +267,7 @@ def postNewOfferFromParsed(swap_client, parsed_data):
|
|||
if 'swap_type' in parsed_data:
|
||||
str_swap_type = parsed_data['swap_type'].lower()
|
||||
swap_type = swap_type_from_string(str_swap_type)
|
||||
elif parsed_data['coin_to'] in (Coins.XMR, Coins.PART_ANON):
|
||||
elif parsed_data['coin_to'] in (Coins.XMR, Coins.WOW, Coins.PART_ANON):
|
||||
swap_type = SwapTypes.XMR_SWAP
|
||||
|
||||
if swap_type == SwapTypes.XMR_SWAP:
|
||||
|
|
|
@ -57,7 +57,7 @@ def page_settings(self, url_split, post_string):
|
|||
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 == 'monero':
|
||||
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)
|
||||
|
@ -104,7 +104,7 @@ def page_settings(self, url_split, post_string):
|
|||
'manage_daemon': c.get('manage_daemon', 'Unknown'),
|
||||
'connection_type': c.get('connection_type', 'Unknown'),
|
||||
})
|
||||
if name == 'monero':
|
||||
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')
|
||||
|
|
|
@ -176,7 +176,7 @@ def page_wallet(self, url_split, post_string):
|
|||
|
||||
if estimate_fee and withdraw:
|
||||
err_messages.append('Estimate fee and withdraw can\'t be used together.')
|
||||
if estimate_fee and coin_id not in (Coins.XMR, ):
|
||||
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}.')
|
||||
|
@ -206,7 +206,7 @@ def page_wallet(self, url_split, post_string):
|
|||
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))
|
||||
elif coin_id == Coins.XMR:
|
||||
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 ''
|
||||
|
@ -281,7 +281,7 @@ def page_wallet(self, url_split, post_string):
|
|||
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 == Coins.XMR:
|
||||
if k in (Coins.XMR, Coins.WOW):
|
||||
wallet_data['main_address'] = w.get('main_address', 'Refresh necessary')
|
||||
elif k == Coins.LTC:
|
||||
wallet_data['mweb_address'] = w.get('mweb_address', 'Refresh necessary')
|
||||
|
|
|
@ -55,6 +55,10 @@ MONERO_VERSION = os.getenv('MONERO_VERSION', '0.18.3.3')
|
|||
MONERO_VERSION_TAG = os.getenv('MONERO_VERSION_TAG', '')
|
||||
XMR_SITE_COMMIT = 'd00169a6decd9470ebdf6a75e3351df4ebcd260a' # Lock hashes.txt to monero version
|
||||
|
||||
WOWNERO_VERSION = os.getenv('WOWNERO_VERSION', '0.11.1.0')
|
||||
WOWNERO_VERSION_TAG = os.getenv('WOWNERO_VERSION_TAG', '')
|
||||
WOW_SITE_COMMIT = '97e100e1605e9f59bc8ca82a5b237d5562c8a21c' # todo
|
||||
|
||||
PIVX_VERSION = os.getenv('PIVX_VERSION', '5.6.1')
|
||||
PIVX_VERSION_TAG = os.getenv('PIVX_VERSION_TAG', '')
|
||||
|
||||
|
@ -85,6 +89,7 @@ known_coins = {
|
|||
'bitcoin': (BITCOIN_VERSION, BITCOIN_VERSION_TAG, ('laanwj',)),
|
||||
'namecoin': ('0.18.0', '', ('JeremyRand',)),
|
||||
'monero': (MONERO_VERSION, MONERO_VERSION_TAG, ('binaryfate',)),
|
||||
'wownero': (WOWNERO_VERSION, WOWNERO_VERSION_TAG, ('wowario',)),
|
||||
'pivx': (PIVX_VERSION, PIVX_VERSION_TAG, ('fuzzbawls',)),
|
||||
'dash': (DASH_VERSION, DASH_VERSION_TAG, ('pasta',)),
|
||||
'firo': (FIRO_VERSION, FIRO_VERSION_TAG, ('reuben',)),
|
||||
|
@ -103,6 +108,7 @@ expected_key_ids = {
|
|||
'laanwj': ('1E4AED62986CD25D',),
|
||||
'JeremyRand': ('2DBE339E29F6294C',),
|
||||
'binaryfate': ('F0AF4D462A0BDF92',),
|
||||
'wowario': ('793504B449C69220',),
|
||||
'davidburkett38': ('3620E9D387E55666',),
|
||||
'fuzzbawls': ('C1ABA64407731FD9',),
|
||||
'pasta': ('52527BEDABE87984',),
|
||||
|
@ -157,6 +163,17 @@ XMR_RPC_USER = os.getenv('XMR_RPC_USER', '')
|
|||
XMR_RPC_PWD = os.getenv('XMR_RPC_PWD', '')
|
||||
DEFAULT_XMR_RESTORE_HEIGHT = int(os.getenv('DEFAULT_XMR_RESTORE_HEIGHT', 2245107))
|
||||
|
||||
WOW_RPC_HOST = os.getenv('WOW_RPC_HOST', '127.0.0.1')
|
||||
BASE_WOW_RPC_PORT = int(os.getenv('BASE_WOW_RPC_PORT', 34598))
|
||||
BASE_WOW_ZMQ_PORT = int(os.getenv('BASE_WOW_ZMQ_PORT', 34698))
|
||||
BASE_WOW_WALLET_PORT = int(os.getenv('BASE_WOW_WALLET_PORT', 34798))
|
||||
WOW_WALLET_RPC_HOST = os.getenv('WOW_WALLET_RPC_HOST', '127.0.0.1')
|
||||
WOW_WALLET_RPC_USER = os.getenv('WOW_WALLET_RPC_USER', 'wow_wallet_user')
|
||||
WOW_WALLET_RPC_PWD = os.getenv('WOW_WALLET_RPC_PWD', 'wow_wallet_pwd')
|
||||
WOW_RPC_USER = os.getenv('WOW_RPC_USER', '')
|
||||
WOW_RPC_PWD = os.getenv('WOW_RPC_PWD', '')
|
||||
DEFAULT_WOW_RESTORE_HEIGHT = int(os.getenv('DEFAULT_WOW_RESTORE_HEIGHT', 450000))
|
||||
|
||||
LTC_RPC_HOST = os.getenv('LTC_RPC_HOST', '127.0.0.1')
|
||||
LTC_RPC_PORT = int(os.getenv('LTC_RPC_PORT', 19895))
|
||||
LTC_ONION_PORT = int(os.getenv('LTC_ONION_PORT', 9333))
|
||||
|
@ -226,12 +243,28 @@ monerod_proxy_config = [
|
|||
'hide-my-port=1', # Don't share the p2p port
|
||||
'p2p-bind-ip=127.0.0.1', # Don't broadcast ip
|
||||
'in-peers=0', # Changes "error" in log to "incoming connections disabled"
|
||||
'out-peers=24',
|
||||
f'tx-proxy=tor,{TOR_PROXY_HOST}:{TOR_PROXY_PORT},disable_noise,16' # Outgoing tx relay to onion
|
||||
]
|
||||
|
||||
monero_wallet_rpc_proxy_config = [
|
||||
'daemon-ssl-allow-any-cert=1',
|
||||
# 'daemon-ssl-allow-any-cert=1', moved to startup flag
|
||||
]
|
||||
|
||||
wownerod_proxy_config = [
|
||||
f'proxy={TOR_PROXY_HOST}:{TOR_PROXY_PORT}',
|
||||
'proxy-allow-dns-leaks=0',
|
||||
'no-igd=1', # Disable UPnP port mapping
|
||||
'hide-my-port=1', # Don't share the p2p port
|
||||
'p2p-bind-ip=127.0.0.1', # Don't broadcast ip
|
||||
'in-peers=0', # Changes "error" in log to "incoming connections disabled"
|
||||
'out-peers=24',
|
||||
f'tx-proxy=tor,{TOR_PROXY_HOST}:{TOR_PROXY_PORT},disable_noise,16' # Outgoing tx relay to onion
|
||||
]
|
||||
|
||||
wownero_wallet_rpc_proxy_config = [
|
||||
# 'daemon-ssl-allow-any-cert=1', moved to startup flag
|
||||
]
|
||||
|
||||
default_socket = socket.socket
|
||||
default_socket_timeout = socket.getdefaulttimeout()
|
||||
|
@ -483,9 +516,9 @@ def extractCore(coin, version_data, settings, bin_dir, release_path, extra_opts=
|
|||
logger.info('extractCore %s v%s%s', coin, version, version_tag)
|
||||
extract_core_overwrite = extra_opts.get('extract_core_overwrite', True)
|
||||
|
||||
if coin in ('monero', 'firo'):
|
||||
if coin == 'monero':
|
||||
bins = ['monerod', 'monero-wallet-rpc']
|
||||
if coin in ('monero', 'firo', 'wownero'):
|
||||
if coin in ('monero', 'wownero'):
|
||||
bins = [coin + 'd', coin + '-wallet-rpc']
|
||||
elif coin == 'firo':
|
||||
bins = [coin + 'd', coin + '-cli', coin + '-tx']
|
||||
else:
|
||||
|
@ -624,6 +657,30 @@ def prepareCore(coin, version_data, settings, data_dir, extra_opts={}):
|
|||
assert_path = os.path.join(bin_dir, assert_filename)
|
||||
if not os.path.exists(assert_path):
|
||||
downloadFile(assert_url, assert_path)
|
||||
elif coin == 'wownero':
|
||||
use_file_ext = 'tar.bz2' if FILE_EXT == 'tar.gz' else FILE_EXT
|
||||
release_filename = '{}-{}-{}.{}'.format(coin, version, BIN_ARCH, use_file_ext)
|
||||
if os_name == 'osx':
|
||||
os_name = 'mac'
|
||||
|
||||
architecture = 'x64'
|
||||
release_url = 'https://git.wownero.com/attachments/280753b0-3af0-4a78-a248-8b925e8f4593'
|
||||
if 'aarch64' in BIN_ARCH:
|
||||
architecture = 'armv8'
|
||||
release_url = 'https://git.wownero.com/attachments/0869ffe3-eeff-4240-a185-168ca80fa1e3'
|
||||
elif 'arm' in BIN_ARCH:
|
||||
architecture = 'armv7' # 32bit doesn't work
|
||||
release_url = 'https://git.wownero.com/attachments/ff0c4886-3865-4670-9bc6-63dd60ded0e3'
|
||||
|
||||
release_path = os.path.join(bin_dir, release_filename)
|
||||
if not os.path.exists(release_path):
|
||||
downloadFile(release_url, release_path)
|
||||
|
||||
assert_filename = 'wownero-{}-hashes.txt'.format(version)
|
||||
assert_url = 'https://git.wownero.com/wownero/wownero.org-website/raw/commit/{}/hashes.txt'.format(WOW_SITE_COMMIT)
|
||||
assert_path = os.path.join(bin_dir, assert_filename)
|
||||
if not os.path.exists(assert_path):
|
||||
downloadFile(assert_url, assert_path)
|
||||
|
||||
elif coin == 'decred':
|
||||
arch_name = BIN_ARCH
|
||||
|
@ -642,6 +699,7 @@ def prepareCore(coin, version_data, settings, data_dir, extra_opts={}):
|
|||
release_filename = '{}-{}-{}.{}'.format(coin, version, arch_name, FILE_EXT)
|
||||
release_page_url = 'https://github.com/decred/decred-binaries/releases/download/v{}'.format(version)
|
||||
release_url = release_page_url + '/' + 'decred-{}-v{}.{}'.format(arch_name, version, FILE_EXT)
|
||||
|
||||
release_path = os.path.join(bin_dir, release_filename)
|
||||
if not os.path.exists(release_path):
|
||||
downloadFile(release_url, release_path)
|
||||
|
@ -789,13 +847,15 @@ def prepareCore(coin, version_data, settings, data_dir, extra_opts={}):
|
|||
pubkeyurls.append('https://raw.githubusercontent.com/dashpay/dash/master/contrib/gitian-keys/pasta.pgp')
|
||||
if coin == 'monero':
|
||||
pubkeyurls.append('https://raw.githubusercontent.com/monero-project/monero/master/utils/gpg_keys/binaryfate.asc')
|
||||
if coin == 'wownero':
|
||||
pubkeyurls.append('https://git.wownero.com/wownero/wownero/raw/branch/master/utils/gpg_keys/wowario.asc')
|
||||
if coin == 'firo':
|
||||
pubkeyurls.append('https://firo.org/reuben.asc')
|
||||
|
||||
if ADD_PUBKEY_URL != '':
|
||||
pubkeyurls.append(ADD_PUBKEY_URL + '/' + pubkey_filename)
|
||||
|
||||
if coin in ('monero', 'firo'):
|
||||
if coin in ('monero', 'wownero', 'firo'):
|
||||
with open(assert_path, 'rb') as fp:
|
||||
verified = gpg.verify_file(fp)
|
||||
|
||||
|
@ -862,7 +922,7 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}):
|
|||
if not os.path.exists(data_dir):
|
||||
os.makedirs(data_dir)
|
||||
|
||||
if coin == 'monero':
|
||||
if coin in ('wownero', 'monero'):
|
||||
core_conf_path = os.path.join(data_dir, coin + 'd.conf')
|
||||
if os.path.exists(core_conf_path):
|
||||
exitWithError('{} exists'.format(core_conf_path))
|
||||
|
@ -886,18 +946,28 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}):
|
|||
fp.write('zmq-rpc-bind-ip={}\n'.format(COINS_RPCBIND_IP))
|
||||
fp.write('prune-blockchain=1\n')
|
||||
|
||||
if tor_control_password is not None:
|
||||
for opt_line in monerod_proxy_config:
|
||||
fp.write(opt_line + '\n')
|
||||
if coin == 'monero':
|
||||
if XMR_RPC_USER != '':
|
||||
fp.write(f'rpc-login={XMR_RPC_USER}:{XMR_RPC_PWD}\n')
|
||||
if tor_control_password is not None:
|
||||
for opt_line in monerod_proxy_config:
|
||||
fp.write(opt_line + '\n')
|
||||
|
||||
if XMR_RPC_USER != '':
|
||||
fp.write(f'rpc-login={XMR_RPC_USER}:{XMR_RPC_PWD}\n')
|
||||
if coin == 'wownero':
|
||||
if WOW_RPC_USER != '':
|
||||
fp.write(f'rpc-login={WOW_RPC_USER}:{WOW_RPC_PWD}\n')
|
||||
if tor_control_password is not None:
|
||||
for opt_line in wownerod_proxy_config:
|
||||
fp.write(opt_line + '\n')
|
||||
|
||||
if coin in ('wownero', 'monero'):
|
||||
wallets_dir = core_settings.get('walletsdir', data_dir)
|
||||
if not os.path.exists(wallets_dir):
|
||||
os.makedirs(wallets_dir)
|
||||
|
||||
wallet_conf_path = os.path.join(wallets_dir, coin + '_wallet.conf')
|
||||
wallet_conf_path = os.path.join(wallets_dir, coin + '-wallet-rpc.conf')
|
||||
if coin == 'monero':
|
||||
wallet_conf_path = os.path.join(wallets_dir, 'monero_wallet.conf')
|
||||
if os.path.exists(wallet_conf_path):
|
||||
exitWithError('{} exists'.format(wallet_conf_path))
|
||||
with open(wallet_conf_path, 'w') as fp:
|
||||
|
@ -911,9 +981,11 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}):
|
|||
fp.write('rpc-bind-ip={}\n'.format(COINS_RPCBIND_IP))
|
||||
fp.write(f'wallet-dir={config_datadir}\n')
|
||||
fp.write('log-file={}\n'.format(os.path.join(config_datadir, 'wallet.log')))
|
||||
fp.write('shared-ringdb-dir={}\n'.format(os.path.join(config_datadir, 'shared-ringdb')))
|
||||
fp.write('rpc-login={}:{}\n'.format(core_settings['walletrpcuser'], core_settings['walletrpcpassword']))
|
||||
|
||||
if coin == 'monero':
|
||||
fp.write('shared-ringdb-dir={}\n'.format(os.path.join(config_datadir, 'shared-ringdb')))
|
||||
elif coin == 'wownero':
|
||||
fp.write('wow-shared-ringdb-dir={}\n'.format(os.path.join(config_datadir, 'shared-ringdb')))
|
||||
if chain == 'regtest':
|
||||
fp.write('allow-mismatched-daemon-version=1\n')
|
||||
|
||||
|
@ -1087,12 +1159,15 @@ def modify_tor_config(settings, coin, tor_control_password=None, enable=False, e
|
|||
coin_settings = settings['chainclients'][coin]
|
||||
data_dir = coin_settings['datadir']
|
||||
|
||||
if coin == 'monero':
|
||||
if coin in ('monero', 'wownero'):
|
||||
core_conf_path = os.path.join(data_dir, coin + 'd.conf')
|
||||
if not os.path.exists(core_conf_path):
|
||||
exitWithError('{} does not exist'.format(core_conf_path))
|
||||
|
||||
wallets_dir = coin_settings.get('walletsdir', data_dir)
|
||||
wallet_conf_path = os.path.join(wallets_dir, coin + '_wallet.conf')
|
||||
wallet_conf_path = os.path.join(wallets_dir, coin + '-wallet-rpc.conf')
|
||||
if coin == 'monero':
|
||||
wallet_conf_path = os.path.join(wallets_dir, 'monero_wallet.conf')
|
||||
if not os.path.exists(wallet_conf_path):
|
||||
exitWithError('{} does not exist'.format(wallet_conf_path))
|
||||
|
||||
|
@ -1105,16 +1180,27 @@ def modify_tor_config(settings, coin, tor_control_password=None, enable=False, e
|
|||
# Disable tor first
|
||||
for line in fp_in:
|
||||
skip_line: bool = False
|
||||
for opt_line in monerod_proxy_config:
|
||||
setting: str = opt_line[0: opt_line.find('=') + 1]
|
||||
if line.startswith(setting):
|
||||
skip_line = True
|
||||
break
|
||||
if coin == 'monero':
|
||||
for opt_line in monerod_proxy_config:
|
||||
setting: str = opt_line[0: opt_line.find('=') + 1]
|
||||
if line.startswith(setting):
|
||||
skip_line = True
|
||||
break
|
||||
if coin == 'wownero':
|
||||
for opt_line in wownerod_proxy_config:
|
||||
setting: str = opt_line[0: opt_line.find('=') + 1]
|
||||
if line.startswith(setting):
|
||||
skip_line = True
|
||||
break
|
||||
if not skip_line:
|
||||
fp.write(line)
|
||||
if enable:
|
||||
for opt_line in monerod_proxy_config:
|
||||
fp.write(opt_line + '\n')
|
||||
if coin == 'monero':
|
||||
for opt_line in monerod_proxy_config:
|
||||
fp.write(opt_line + '\n')
|
||||
if coin == 'wownero':
|
||||
for opt_line in wownerod_proxy_config:
|
||||
fp.write(opt_line + '\n')
|
||||
|
||||
with open(wallet_conf_path, 'w') as fp:
|
||||
with open(wallet_conf_path + '.last') as fp_in:
|
||||
|
@ -1210,6 +1296,7 @@ def printHelp():
|
|||
print('--htmlhost= Interface to host html server on, default:127.0.0.1.')
|
||||
print('--wshost= Interface to host websocket server on, disable by setting to "none", default\'s to --htmlhost.')
|
||||
print('--xmrrestoreheight=n Block height to restore Monero wallet from, default:{}.'.format(DEFAULT_XMR_RESTORE_HEIGHT))
|
||||
print('--wowrestoreheight=n Block height to restore Wownero wallet from, default:{}.'.format(DEFAULT_WOW_RESTORE_HEIGHT))
|
||||
print('--trustremotenode Set trusted-daemon for XMR, defaults to auto: true when daemon rpchost value is a private ip address else false')
|
||||
print('--noextractover Prevent extracting cores if files exist. Speeds up tests')
|
||||
print('--usetorproxy Use TOR proxy during setup. Note that some download links may be inaccessible over TOR.')
|
||||
|
@ -1305,7 +1392,11 @@ def initialise_wallets(particl_wallet_mnemonic, with_coins, data_dir, settings,
|
|||
|
||||
if c == Coins.XMR:
|
||||
if coin_settings['manage_wallet_daemon']:
|
||||
filename = 'monero-wallet-rpc' + ('.exe' if os.name == 'nt' else '')
|
||||
filename = coin_name + '-wallet-rpc' + ('.exe' if os.name == 'nt' else '')
|
||||
daemons.append(startXmrWalletDaemon(coin_settings['datadir'], coin_settings['bindir'], filename))
|
||||
elif c == Coins.WOW:
|
||||
if coin_settings['manage_wallet_daemon']:
|
||||
filename = coin_name + '-wallet-rpc' + ('.exe' if os.name == 'nt' else '')
|
||||
daemons.append(startXmrWalletDaemon(coin_settings['datadir'], coin_settings['bindir'], filename))
|
||||
elif c == Coins.DCR:
|
||||
pass
|
||||
|
@ -1470,6 +1561,7 @@ def main():
|
|||
coins_changed = False
|
||||
htmlhost = '127.0.0.1'
|
||||
xmr_restore_height = DEFAULT_XMR_RESTORE_HEIGHT
|
||||
wow_restore_height = DEFAULT_WOW_RESTORE_HEIGHT
|
||||
prepare_bin_only = False
|
||||
no_cores = False
|
||||
enable_tor = False
|
||||
|
@ -1585,6 +1677,9 @@ def main():
|
|||
if name == 'xmrrestoreheight':
|
||||
xmr_restore_height = int(s[1])
|
||||
continue
|
||||
if name == 'wowrestoreheight':
|
||||
wow_restore_height = int(s[1])
|
||||
continue
|
||||
if name == 'keysdirpath':
|
||||
extra_opts['keysdirpath'] = os.path.expanduser(s[1].strip('"'))
|
||||
continue
|
||||
|
@ -1825,6 +1920,28 @@ def main():
|
|||
'core_version_group': 18,
|
||||
'chain_lookups': 'local',
|
||||
'startup_tries': 40,
|
||||
},
|
||||
'wownero': {
|
||||
'connection_type': 'rpc' if 'wownero' in with_coins else 'none',
|
||||
'manage_daemon': True if ('wownero' in with_coins and WOW_RPC_HOST == '127.0.0.1') else False,
|
||||
'manage_wallet_daemon': True if ('wownero' in with_coins and WOW_WALLET_RPC_HOST == '127.0.0.1') else False,
|
||||
'rpcport': BASE_WOW_RPC_PORT + port_offset,
|
||||
'zmqport': BASE_WOW_ZMQ_PORT + port_offset,
|
||||
'walletrpcport': BASE_WOW_WALLET_PORT + port_offset,
|
||||
'rpchost': WOW_RPC_HOST,
|
||||
'trusted_daemon': extra_opts.get('trust_remote_node', 'auto'),
|
||||
'walletrpchost': WOW_WALLET_RPC_HOST,
|
||||
'walletrpcuser': WOW_WALLET_RPC_USER,
|
||||
'walletrpcpassword': WOW_WALLET_RPC_PWD,
|
||||
'walletfile': 'swap_wallet',
|
||||
'datadir': os.getenv('WOW_DATA_DIR', os.path.join(data_dir, 'wownero')),
|
||||
'bindir': os.path.join(bin_dir, 'wownero'),
|
||||
'restore_height': wow_restore_height,
|
||||
'blocks_confirmed': 2,
|
||||
'rpctimeout': 60,
|
||||
'walletrpctimeout': 120,
|
||||
'walletrpctimeoutlong': 300,
|
||||
'core_type_group': 'xmr',
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1840,6 +1957,9 @@ def main():
|
|||
if XMR_RPC_USER != '':
|
||||
chainclients['monero']['rpcuser'] = XMR_RPC_USER
|
||||
chainclients['monero']['rpcpassword'] = XMR_RPC_PWD
|
||||
if WOW_RPC_USER != '':
|
||||
chainclients['wownero']['rpcuser'] = WOW_RPC_USER
|
||||
chainclients['wownero']['rpcpassword'] = WOW_RPC_PWD
|
||||
if PIVX_RPC_USER != '':
|
||||
chainclients['pivx']['rpcuser'] = PIVX_RPC_USER
|
||||
chainclients['pivx']['rpcpassword'] = PIVX_RPC_PWD
|
||||
|
@ -1854,6 +1974,7 @@ def main():
|
|||
chainclients['nav']['rpcpassword'] = NAV_RPC_PWD
|
||||
|
||||
chainclients['monero']['walletsdir'] = os.getenv('XMR_WALLETS_DIR', chainclients['monero']['datadir'])
|
||||
chainclients['wownero']['walletsdir'] = os.getenv('WOW_WALLETS_DIR', chainclients['wownero']['datadir'])
|
||||
|
||||
if initwalletsonly:
|
||||
logger.info('Initialising wallets')
|
||||
|
|
|
@ -98,11 +98,11 @@ def startDaemon(node_dir, bin_dir, daemon_bin, opts=[], extra_config={}):
|
|||
|
||||
|
||||
def startXmrDaemon(node_dir, bin_dir, daemon_bin, opts=[]):
|
||||
daemon_bin = os.path.expanduser(os.path.join(bin_dir, daemon_bin))
|
||||
daemon_path = os.path.expanduser(os.path.join(bin_dir, daemon_bin))
|
||||
|
||||
datadir_path = os.path.expanduser(node_dir)
|
||||
args = [daemon_bin, '--non-interactive', '--config-file=' + os.path.join(datadir_path, 'monerod.conf')] + opts
|
||||
logging.info('Starting node {} --data-dir={}'.format(daemon_bin, node_dir))
|
||||
args = [daemon_path, '--non-interactive', '--config-file=' + os.path.join(datadir_path, daemon_bin + '.conf')] + opts
|
||||
logging.info('Starting node {} --data-dir={}'.format(daemon_path, node_dir))
|
||||
|
||||
# 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')
|
||||
|
@ -129,7 +129,7 @@ def startXmrWalletDaemon(node_dir, bin_dir, wallet_bin, opts=[]):
|
|||
args += opts
|
||||
|
||||
if needs_rewrite:
|
||||
logging.info('Rewriting monero_wallet.conf')
|
||||
logging.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:
|
||||
|
@ -208,10 +208,10 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
|||
except Exception as e:
|
||||
logger.warning('Not starting unknown coin: {}'.format(c))
|
||||
continue
|
||||
if c == 'monero':
|
||||
if c in ('monero', 'wownero'):
|
||||
if v['manage_daemon'] is True:
|
||||
swap_client.log.info(f'Starting {display_name} daemon')
|
||||
filename = 'monerod' + ('.exe' if os.name == 'nt' else '')
|
||||
filename = c + 'd' + ('.exe' if os.name == 'nt' else '')
|
||||
daemons.append(startXmrDaemon(v['datadir'], v['bindir'], filename))
|
||||
pid = daemons[-1].handle.pid
|
||||
swap_client.log.info('Started {} {}'.format(filename, pid))
|
||||
|
@ -226,7 +226,7 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
|||
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}', ]
|
||||
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))
|
||||
|
||||
|
@ -237,7 +237,7 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
|||
opts.append(daemon_rpcuser + ':' + daemon_rpcpass)
|
||||
|
||||
opts.append('--trusted-daemon' if trusted_daemon else '--untrusted-daemon')
|
||||
filename = 'monero-wallet-rpc' + ('.exe' if os.name == 'nt' else '')
|
||||
filename = c + '-wallet-rpc' + ('.exe' if os.name == 'nt' else '')
|
||||
daemons.append(startXmrWalletDaemon(v['datadir'], v['bindir'], filename, opts))
|
||||
pid = daemons[-1].handle.pid
|
||||
swap_client.log.info('Started {} {}'.format(filename, pid))
|
||||
|
|
16
docker/production/compose-fragments/1_wownero-wallet.yml
Normal file
16
docker/production/compose-fragments/1_wownero-wallet.yml
Normal file
|
@ -0,0 +1,16 @@
|
|||
wownero_wallet:
|
||||
image: i_wownero_wallet
|
||||
build:
|
||||
context: wownero_wallet
|
||||
dockerfile: Dockerfile
|
||||
container_name: wownero_wallet
|
||||
volumes:
|
||||
- ${DATA_PATH}/wownero_wallet:/data
|
||||
expose:
|
||||
- ${BASE_WOW_WALLET_PORT}
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
restart: unless-stopped
|
16
docker/production/compose-fragments/8_wownero-daemon.yml
Normal file
16
docker/production/compose-fragments/8_wownero-daemon.yml
Normal file
|
@ -0,0 +1,16 @@
|
|||
wownero_daemon:
|
||||
image: i_wownero_daemon
|
||||
build:
|
||||
context: wownero_daemon
|
||||
dockerfile: Dockerfile
|
||||
container_name: wownero_daemon
|
||||
volumes:
|
||||
- ${DATA_PATH}/wownero_daemon:/data
|
||||
expose:
|
||||
- ${BASE_WOW_RPC_PORT}
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
restart: unless-stopped
|
|
@ -8,6 +8,8 @@
|
|||
- ${DATA_PATH}/swapclient:/data/swapclient
|
||||
- ${DATA_PATH}/monero_daemon:/data/monero_daemon
|
||||
- ${DATA_PATH}/monero_wallet:/data/monero_wallet
|
||||
- ${DATA_PATH}/wownero_daemon:/data/wownero_daemon
|
||||
- ${DATA_PATH}/wownero_wallet:/data/wownero_wallet
|
||||
- ${DATA_PATH}/particl:/data/particl
|
||||
- ${DATA_PATH}/bitcoin:/data/bitcoin
|
||||
- ${DATA_PATH}/litecoin:/data/litecoin
|
||||
|
@ -45,6 +47,16 @@
|
|||
- XMR_WALLET_RPC_USER
|
||||
- XMR_WALLET_RPC_PWD
|
||||
- DEFAULT_XMR_RESTORE_HEIGHT
|
||||
- WOW_DATA_DIR
|
||||
- WOW_RPC_HOST
|
||||
- BASE_WOW_RPC_PORT
|
||||
- BASE_WOW_ZMQ_PORT
|
||||
- WOW_WALLETS_DIR
|
||||
- WOW_WALLET_RPC_HOST
|
||||
- BASE_WOW_WALLET_PORT
|
||||
- WOW_WALLET_RPC_USER
|
||||
- WOW_WALLET_RPC_PWD
|
||||
- DEFAULT_WOW_RESTORE_HEIGHT
|
||||
- PIVX_DATA_DIR
|
||||
- PIVX_RPC_HOST
|
||||
- PIVX_RPC_PORT
|
||||
|
|
|
@ -44,6 +44,16 @@ BASE_XMR_WALLET_PORT=29998
|
|||
XMR_WALLET_RPC_USER=xmr_wallet_user
|
||||
XMR_WALLET_RPC_PWD=xmr_wallet_pwd
|
||||
|
||||
WOW_DATA_DIR=/data/wownero_daemon
|
||||
WOW_RPC_HOST=wownero_daemon
|
||||
BASE_WOW_RPC_PORT=34598
|
||||
|
||||
WOW_WALLETS_DIR=/data/wownero_wallet
|
||||
WOW_WALLET_RPC_HOST=wownero_wallet
|
||||
BASE_WOW_WALLET_PORT=34798
|
||||
WOW_WALLET_RPC_USER=wow_wallet_user
|
||||
WOW_WALLET_RPC_PWD=wow_wallet_pwd
|
||||
|
||||
PIVX_DATA_DIR=/data/pivx
|
||||
PIVX_RPC_HOST=pivx_core
|
||||
PIVX_RPC_PORT=51473
|
||||
|
|
|
@ -57,12 +57,12 @@ def main():
|
|||
if coin_name == 'particl':
|
||||
# Nothing to do
|
||||
continue
|
||||
if coin_name == 'monero':
|
||||
with open(os.path.join(fragments_dir, '1_monero-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_monero-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
|
||||
|
|
24
docker/production/wownero_daemon/Dockerfile
Normal file
24
docker/production/wownero_daemon/Dockerfile
Normal file
|
@ -0,0 +1,24 @@
|
|||
FROM i_swapclient as install_stage
|
||||
|
||||
RUN basicswap-prepare --preparebinonly --bindir=/coin_bin --withcoin=wownero --withoutcoins=particl
|
||||
|
||||
FROM debian:bullseye-slim
|
||||
|
||||
COPY --from=install_stage /coin_bin .
|
||||
|
||||
ENV WOWNERO_DATA /data
|
||||
|
||||
RUN groupadd -r wownero && useradd -r -m -g wownero wownero \
|
||||
&& apt-get update \
|
||||
&& apt-get install -qq --no-install-recommends gosu \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& mkdir -p "$WOWNERO_DATA" \
|
||||
&& chown -R wownero:wownero "$WOWNERO_DATA" \
|
||||
&& ln -sfn "$WOWNERO_DATA" /home/wownero/.wownero \
|
||||
&& chown -h wownero:wownero /home/wownero/.wownero
|
||||
VOLUME $WOWNERO_DATA
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
||||
CMD ["/wownero/wownerod", "--non-interactive", "--config-file=/home/wownero/.wownero/wownerod.conf", "--confirm-external-bind"]
|
11
docker/production/wownero_daemon/entrypoint.sh
Executable file
11
docker/production/wownero_daemon/entrypoint.sh
Executable file
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
if [[ "$1" == "wownerod" ]]; then
|
||||
mkdir -p "$WOWNERO_DATA"
|
||||
|
||||
chown -h wownero:wownero /home/wownero/.wownero
|
||||
exec gosu wownero "$@"
|
||||
else
|
||||
exec "$@"
|
||||
fi
|
16
docker/production/wownero_wallet/Dockerfile
Normal file
16
docker/production/wownero_wallet/Dockerfile
Normal file
|
@ -0,0 +1,16 @@
|
|||
FROM i_wownero_daemon
|
||||
|
||||
ENV WOWNERO_DATA /data
|
||||
|
||||
RUN groupadd -r wownero_wallet && useradd -r -m -g wownero_wallet wownero_wallet \
|
||||
&& apt-get update \
|
||||
&& apt-get install -qq --no-install-recommends gosu \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& mkdir -p "$WOWNERO_DATA" \
|
||||
&& chown -R wownero_wallet:wownero_wallet "$WOWNERO_DATA"
|
||||
VOLUME $WOWNERO_DATA
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
||||
CMD ["/wownero/wownero-wallet-rpc", "--non-interactive", "--config-file=/data/wownero-wallet-rpc.conf", "--confirm-external-bind"]
|
11
docker/production/wownero_wallet/entrypoint.sh
Executable file
11
docker/production/wownero_wallet/entrypoint.sh
Executable file
|
@ -0,0 +1,11 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
if [[ "$1" == "wownero-wallet-rpc" ]]; then
|
||||
mkdir -p "$WOWNERO_DATA"
|
||||
|
||||
chown -h wownero_wallet:wownero_wallet /data
|
||||
exec gosu wownero_wallet "$@"
|
||||
else
|
||||
exec "$@"
|
||||
fi
|
1
setup.py
1
setup.py
|
@ -29,6 +29,7 @@ setuptools.setup(
|
|||
"particl",
|
||||
"bitcoin",
|
||||
"monero",
|
||||
"wownero",
|
||||
],
|
||||
install_requires=[
|
||||
"wheel",
|
||||
|
|
1
tox.ini
1
tox.ini
|
@ -11,6 +11,7 @@ passenv =
|
|||
BITCOIN_BINDIR
|
||||
LITECOIN_BINDIR
|
||||
XMR_BINDIR
|
||||
WOW_BINDIR
|
||||
TEST_PREPARE_PATH
|
||||
TEST_RELOAD_PATH
|
||||
deps =
|
||||
|
|
Loading…
Reference in a new issue