mirror of
https://github.com/basicswap/basicswap.git
synced 2024-11-16 15:58:17 +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 . import __version__
|
||||||
from .rpc import escape_rpcauth
|
from .rpc import escape_rpcauth
|
||||||
from .rpc_xmr import make_xmr_rpc2_func
|
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 .ui.util import getCoinName, known_chart_coins
|
||||||
from .util import (
|
from .util import (
|
||||||
AutomationConstraint,
|
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
|
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):
|
def threadPollChainState(swap_client, coin_type):
|
||||||
ci = swap_client.ci(coin_type)
|
ci = swap_client.ci(coin_type)
|
||||||
cc = swap_client.coin_clients[coin_type]
|
cc = swap_client.coin_clients[coin_type]
|
||||||
|
@ -281,7 +297,7 @@ class BasicSwap(BaseApp):
|
||||||
|
|
||||||
# TODO: Set dynamically
|
# TODO: Set dynamically
|
||||||
self.balance_only_coins = (Coins.LTC_MWEB, )
|
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.adaptor_swap_only_coins = self.scriptless_coins + (Coins.PART_BLIND, )
|
||||||
self.coins_without_segwit = (Coins.PIVX, Coins.DASH, Coins.NMC)
|
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 self.coin_clients[coin]['connection_type'] == 'rpc':
|
||||||
if coin == Coins.DCR:
|
if coin == Coins.DCR:
|
||||||
self.coin_clients[coin]['walletrpcport'] = chain_client_settings['walletrpcport']
|
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]['rpctimeout'] = chain_client_settings.get('rpctimeout', 60)
|
||||||
self.coin_clients[coin]['walletrpctimeout'] = chain_client_settings.get('walletrpctimeout', 120)
|
self.coin_clients[coin]['walletrpctimeout'] = chain_client_settings.get('walletrpctimeout', 120)
|
||||||
self.coin_clients[coin]['walletrpctimeoutlong'] = chain_client_settings.get('walletrpctimeoutlong', 600)
|
self.coin_clients[coin]['walletrpctimeoutlong'] = chain_client_settings.get('walletrpctimeoutlong', 600)
|
||||||
|
@ -561,9 +577,9 @@ class BasicSwap(BaseApp):
|
||||||
if self.use_tor_proxy:
|
if self.use_tor_proxy:
|
||||||
have_cc_tor_opt = 'use_tor' in chain_client_settings
|
have_cc_tor_opt = 'use_tor' in chain_client_settings
|
||||||
if have_cc_tor_opt and chain_client_settings['use_tor'] is False:
|
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):
|
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:
|
else:
|
||||||
proxy_host = self.tor_proxy_host
|
proxy_host = self.tor_proxy_host
|
||||||
proxy_port = self.tor_proxy_port
|
proxy_port = self.tor_proxy_port
|
||||||
|
@ -585,7 +601,10 @@ class BasicSwap(BaseApp):
|
||||||
if proxy_host:
|
if proxy_host:
|
||||||
self.log.info(f'Connecting through proxy at {proxy_host}.')
|
self.log.info(f'Connecting through proxy at {proxy_host}.')
|
||||||
|
|
||||||
|
if coin == Coins.XMR:
|
||||||
return make_xmr_rpc2_func(rpcport, daemon_login, rpchost, proxy_host=proxy_host, proxy_port=proxy_port)
|
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
|
daemon_login = None
|
||||||
if coin_settings.get('rpcuser', '') != '':
|
if coin_settings.get('rpcuser', '') != '':
|
||||||
|
@ -687,6 +706,12 @@ class BasicSwap(BaseApp):
|
||||||
chain_client_settings = self.getChainClientSettings(coin)
|
chain_client_settings = self.getChainClientSettings(coin)
|
||||||
xmr_i.setWalletFilename(chain_client_settings['walletfile'])
|
xmr_i.setWalletFilename(chain_client_settings['walletfile'])
|
||||||
return xmr_i
|
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:
|
elif coin == Coins.PIVX:
|
||||||
from .interface.pivx import PIVXInterface
|
from .interface.pivx import PIVXInterface
|
||||||
return PIVXInterface(self.coin_clients[coin], self.chain, self)
|
return PIVXInterface(self.coin_clients[coin], self.chain, self)
|
||||||
|
@ -711,7 +736,7 @@ class BasicSwap(BaseApp):
|
||||||
|
|
||||||
def setCoinRunParams(self, coin):
|
def setCoinRunParams(self, coin):
|
||||||
cc = self.coin_clients[coin]
|
cc = self.coin_clients[coin]
|
||||||
if coin == Coins.XMR:
|
if coin in (Coins.XMR, Coins.WOW):
|
||||||
return
|
return
|
||||||
if cc['connection_type'] == 'rpc' and cc['rpcauth'] is None:
|
if cc['connection_type'] == 'rpc' and cc['rpcauth'] is None:
|
||||||
chain_client_settings = self.getChainClientSettings(coin)
|
chain_client_settings = self.getChainClientSettings(coin)
|
||||||
|
@ -782,8 +807,14 @@ class BasicSwap(BaseApp):
|
||||||
core_version = ci.getDaemonVersion()
|
core_version = ci.getDaemonVersion()
|
||||||
self.log.info('%s Core version %d', ci.coin_name(), core_version)
|
self.log.info('%s Core version %d', ci.coin_name(), core_version)
|
||||||
self.coin_clients[c]['core_version'] = 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))
|
t = threading.Thread(target=thread_func, args=(self, c))
|
||||||
self.threads.append(t)
|
self.threads.append(t)
|
||||||
t.start()
|
t.start()
|
||||||
|
@ -808,6 +839,12 @@ class BasicSwap(BaseApp):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log.warning('Can\'t open XMR wallet, could be locked.')
|
self.log.warning('Can\'t open XMR wallet, could be locked.')
|
||||||
continue
|
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:
|
elif c == Coins.LTC:
|
||||||
ci_mweb = self.ci(Coins.LTC_MWEB)
|
ci_mweb = self.ci(Coins.LTC_MWEB)
|
||||||
is_encrypted, _ = self.getLockedState()
|
is_encrypted, _ = self.getLockedState()
|
||||||
|
@ -858,7 +895,7 @@ class BasicSwap(BaseApp):
|
||||||
self.log.info('Scanned %d unread messages.', nm)
|
self.log.info('Scanned %d unread messages.', nm)
|
||||||
|
|
||||||
def stopDaemon(self, coin) -> None:
|
def stopDaemon(self, coin) -> None:
|
||||||
if coin in (Coins.XMR, Coins.DCR):
|
if coin in (Coins.XMR, Coins.DCR, Coins.WOW):
|
||||||
return
|
return
|
||||||
num_tries = 10
|
num_tries = 10
|
||||||
authcookiepath = os.path.join(self.getChainDatadirPath(coin), '.cookie')
|
authcookiepath = os.path.join(self.getChainDatadirPath(coin), '.cookie')
|
||||||
|
@ -897,7 +934,7 @@ class BasicSwap(BaseApp):
|
||||||
|
|
||||||
if with_wallet:
|
if with_wallet:
|
||||||
self.waitForDaemonRPC(coin_type, with_wallet=False)
|
self.waitForDaemonRPC(coin_type, with_wallet=False)
|
||||||
if coin_type in (Coins.XMR,):
|
if coin_type in (Coins.XMR, Coins.WOW):
|
||||||
return
|
return
|
||||||
ci = self.ci(coin_type)
|
ci = self.ci(coin_type)
|
||||||
# checkWallets can adjust the wallet name.
|
# 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()))
|
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':
|
if self.coin_clients[c]['connection_type'] != 'rpc':
|
||||||
continue
|
continue
|
||||||
if c == Coins.XMR:
|
if c in (Coins.XMR, Coins.WOW):
|
||||||
continue # TODO
|
continue # TODO
|
||||||
synced = round(ci.getBlockchainInfo()['verificationprogress'], 3)
|
synced = round(ci.getBlockchainInfo()['verificationprogress'], 3)
|
||||||
if synced < 1.0:
|
if synced < 1.0:
|
||||||
|
@ -1035,7 +1072,7 @@ class BasicSwap(BaseApp):
|
||||||
db_key_coin_name = ci.coin_name().lower()
|
db_key_coin_name = ci.coin_name().lower()
|
||||||
self.log.info('Initialising {} wallet.'.format(ci.coin_name()))
|
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_view = self.getWalletKey(coin_type, 1, for_ed25519=True)
|
||||||
key_spend = self.getWalletKey(coin_type, 2, for_ed25519=True)
|
key_spend = self.getWalletKey(coin_type, 2, for_ed25519=True)
|
||||||
ci.initialiseWallet(key_view, key_spend)
|
ci.initialiseWallet(key_view, key_spend)
|
||||||
|
@ -1951,7 +1988,7 @@ class BasicSwap(BaseApp):
|
||||||
return self.ci(coin_type).get_fee_rate(conf_target)
|
return self.ci(coin_type).get_fee_rate(conf_target)
|
||||||
|
|
||||||
def estimateWithdrawFee(self, coin_type, fee_rate):
|
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
|
# Fee estimate must be manually initiated
|
||||||
return None
|
return None
|
||||||
tx_vsize = self.ci(coin_type).getHTLCSpendTxVSize()
|
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:
|
def withdrawCoin(self, coin_type, value, addr_to, subfee: bool) -> str:
|
||||||
ci = self.ci(coin_type)
|
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))
|
self.log.info('withdrawCoin sweep all {} to {}'.format(ci.ticker(), addr_to))
|
||||||
else:
|
else:
|
||||||
self.log.info('withdrawCoin {} {} to {} {}'.format(value, ci.ticker(), addr_to, ' subfee' if subfee 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:
|
if c == Coins.PART:
|
||||||
ci.setWalletSeedWarning(False) # All keys should be be derived from the Particl mnemonic
|
ci.setWalletSeedWarning(False) # All keys should be be derived from the Particl mnemonic
|
||||||
return True # TODO
|
return True # TODO
|
||||||
if c == Coins.XMR:
|
if c in (Coins.XMR, Coins.WOW):
|
||||||
expect_address = self.getCachedMainWalletAddress(ci)
|
expect_address = self.getCachedMainWalletAddress(ci)
|
||||||
if expect_address is None:
|
if expect_address is None:
|
||||||
self.log.warning('Can\'t find expected main wallet address for coin {}'.format(ci.coin_name()))
|
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?
|
# TODO: How to scan pruned blocks?
|
||||||
|
|
||||||
if not self.checkWalletSeed(coin_type):
|
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?')
|
raise ValueError('TODO: How to reseed XMR wallet?')
|
||||||
else:
|
else:
|
||||||
raise ValueError('Wallet seed doesn\'t match expected.')
|
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)
|
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, KeyTypes.KBSL, for_ed25519)
|
||||||
vkbs = ci_to.sumKeys(kbsl, kbsf)
|
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)
|
address_to = self.getCachedMainWalletAddress(ci_to, session)
|
||||||
elif coin_to in (Coins.PART_BLIND, Coins.PART_ANON):
|
elif coin_to in (Coins.PART_BLIND, Coins.PART_ANON):
|
||||||
address_to = self.getCachedStealthAddressForCoin(coin_to, session)
|
address_to = self.getCachedStealthAddressForCoin(coin_to, session)
|
||||||
|
@ -5932,7 +5969,7 @@ class BasicSwap(BaseApp):
|
||||||
vkbs = ci_to.sumKeys(kbsl, kbsf)
|
vkbs = ci_to.sumKeys(kbsl, kbsf)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if offer.coin_to == Coins.XMR:
|
if offer.coin_to in (Coins.XMR, Coins.WOW):
|
||||||
address_to = self.getCachedMainWalletAddress(ci_to, session)
|
address_to = self.getCachedMainWalletAddress(ci_to, session)
|
||||||
elif coin_to in (Coins.PART_BLIND, Coins.PART_ANON):
|
elif coin_to in (Coins.PART_BLIND, Coins.PART_ANON):
|
||||||
address_to = self.getCachedStealthAddressForCoin(coin_to, session)
|
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['anon_pending'] = walletinfo['unconfirmed_anon'] + walletinfo['immature_anon_balance']
|
||||||
rv['blind_balance'] = walletinfo['blind_balance']
|
rv['blind_balance'] = walletinfo['blind_balance']
|
||||||
rv['blind_unconfirmed'] = walletinfo['unconfirmed_blind']
|
rv['blind_unconfirmed'] = walletinfo['unconfirmed_blind']
|
||||||
elif coin == Coins.XMR:
|
elif coin in (Coins.XMR, Coins.WOW):
|
||||||
rv['main_address'] = self.getCachedMainWalletAddress(ci)
|
rv['main_address'] = self.getCachedMainWalletAddress(ci)
|
||||||
elif coin == Coins.NAV:
|
elif coin == Coins.NAV:
|
||||||
rv['immature'] = walletinfo['immature_balance']
|
rv['immature'] = walletinfo['immature_balance']
|
||||||
|
|
|
@ -10,6 +10,7 @@ from .util import (
|
||||||
)
|
)
|
||||||
|
|
||||||
XMR_COIN = 10 ** 12
|
XMR_COIN = 10 ** 12
|
||||||
|
WOW_COIN = 10 ** 11
|
||||||
|
|
||||||
|
|
||||||
class Coins(IntEnum):
|
class Coins(IntEnum):
|
||||||
|
@ -21,13 +22,14 @@ class Coins(IntEnum):
|
||||||
XMR = 6
|
XMR = 6
|
||||||
PART_BLIND = 7
|
PART_BLIND = 7
|
||||||
PART_ANON = 8
|
PART_ANON = 8
|
||||||
# ZANO = 9
|
WOW = 9
|
||||||
# NDAU = 10
|
# NDAU = 10
|
||||||
PIVX = 11
|
PIVX = 11
|
||||||
DASH = 12
|
DASH = 12
|
||||||
FIRO = 13
|
FIRO = 13
|
||||||
NAV = 14
|
NAV = 14
|
||||||
LTC_MWEB = 15
|
LTC_MWEB = 15
|
||||||
|
# ZANO = 16 was 9
|
||||||
|
|
||||||
|
|
||||||
chainparams = {
|
chainparams = {
|
||||||
|
@ -247,6 +249,33 @@ chainparams = {
|
||||||
'address_prefix': 18,
|
'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: {
|
Coins.PIVX: {
|
||||||
'name': 'pivx',
|
'name': 'pivx',
|
||||||
'ticker': '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')))
|
XMR_BINDIR = os.path.expanduser(os.getenv('XMR_BINDIR', os.path.join(DEFAULT_TEST_BINDIR, 'monero')))
|
||||||
XMRD = os.getenv('XMRD', 'monerod' + bin_suffix)
|
XMRD = os.getenv('XMRD', 'monerod' + bin_suffix)
|
||||||
XMR_WALLET_RPC = os.getenv('XMR_WALLET_RPC', 'monero-wallet-rpc' + 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:
|
else:
|
||||||
raise ValueError('Unknown RPC variant')
|
raise ValueError('Unknown RPC variant')
|
||||||
result = json.dumps(rv, indent=4)
|
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:
|
else:
|
||||||
if call_type == 'http':
|
if call_type == 'http':
|
||||||
ci = swap_client.ci(coin_type)
|
ci = swap_client.ci(coin_type)
|
||||||
|
@ -338,6 +354,7 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
coin_available = listAvailableCoins(swap_client, with_variants=False)
|
coin_available = listAvailableCoins(swap_client, with_variants=False)
|
||||||
with_xmr: bool = any(c[0] == Coins.XMR for c in coin_available)
|
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 = [(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):
|
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)) + ',0', 'Monero'))
|
||||||
coins.append((str(int(Coins.XMR)) + ',1', 'Monero JSON'))
|
coins.append((str(int(Coins.XMR)) + ',1', 'Monero JSON'))
|
||||||
coins.append((str(int(Coins.XMR)) + ',2', 'Monero Wallet'))
|
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, {
|
return self.render_template(template, {
|
||||||
'messages': messages,
|
'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)
|
post_data = getFormData(post_string, is_json)
|
||||||
address = get_data_entry(post_data, 'address')
|
address = get_data_entry(post_data, 'address')
|
||||||
|
|
||||||
if coin_type == Coins.XMR:
|
if coin_type in (Coins.XMR, Coins.WOW):
|
||||||
value = None
|
value = None
|
||||||
sweepall = get_data_entry(post_data, 'sweepall')
|
sweepall = get_data_entry(post_data, 'sweepall')
|
||||||
if not isinstance(sweepall, bool):
|
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:
|
elif coin_type == Coins.LTC:
|
||||||
type_from = get_data_entry_or(post_data, 'type_from', 'plain')
|
type_from = get_data_entry_or(post_data, 'type_from', 'plain')
|
||||||
txid_hex = swap_client.withdrawLTC(type_from, value, address, subfee)
|
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)
|
txid_hex = swap_client.withdrawCoin(coin_type, value, address, sweepall)
|
||||||
else:
|
else:
|
||||||
txid_hex = swap_client.withdrawCoin(coin_type, value, address, subfee)
|
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.')
|
raise ValueError('Particl wallet seed is set from the Basicswap mnemonic.')
|
||||||
|
|
||||||
ci = swap_client.ci(coin)
|
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_view = swap_client.getWalletKey(coin, 1, for_ed25519=True)
|
||||||
key_spend = swap_client.getWalletKey(coin, 2, for_ed25519=True)
|
key_spend = swap_client.getWalletKey(coin, 2, for_ed25519=True)
|
||||||
address = ci.getAddressFromKeys(key_view, key_spend)
|
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);
|
document.getElementById('get_rate_inferred_button').addEventListener('click', getRateInferred);
|
||||||
|
|
||||||
function set_swap_type_enabled(coin_from, coin_to, swap_type) {
|
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 */];
|
const secret_hash_only_coins = ['11' /* PIVX */, '12' /* DASH */];
|
||||||
let make_hidden = false;
|
let make_hidden = false;
|
||||||
if (adaptor_sig_only_coins.includes(coin_from) || adaptor_sig_only_coins.includes(coin_to)) {
|
if (adaptor_sig_only_coins.includes(coin_from) || adaptor_sig_only_coins.includes(coin_to)) {
|
||||||
|
|
|
@ -105,7 +105,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if c.manage_daemon is defined %}
|
{% 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">
|
<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 bold">Manage Daemon</td>
|
||||||
<td class="py-3 px-6">
|
<td class="py-3 px-6">
|
||||||
|
@ -175,7 +175,7 @@
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% if c.name == 'monero' %}
|
{% if c.name in ('wownero', 'monero') %}
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
<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 bold">Transaction Fee Priority</td>
|
||||||
<td class="py-3 px-6">
|
<td class="py-3 px-6">
|
||||||
|
|
|
@ -266,8 +266,8 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{# / PART #}
|
{# / PART #}
|
||||||
{% if w.cid == '6' %}
|
{% if w.cid in '6, 9' %}
|
||||||
{# XMR #}
|
{# XMR | WOW #}
|
||||||
<div class="w-full md:w-1/2 p-3 flex justify-center items-center">
|
<div class="w-full md:w-1/2 p-3 flex justify-center items-center">
|
||||||
<div class="h-full">
|
<div class="h-full">
|
||||||
<div class="flex flex-wrap -m-3">
|
<div class="flex flex-wrap -m-3">
|
||||||
|
@ -412,8 +412,8 @@
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if w.cid == '6' %}
|
{% if w.cid in '6, 9' %}
|
||||||
{# XMR #}
|
{# XMR | WOW #}
|
||||||
<script>
|
<script>
|
||||||
// Monero Sub
|
// Monero Sub
|
||||||
var moneroSubAddress = "{{ w.deposit_address }}";
|
var moneroSubAddress = "{{ w.deposit_address }}";
|
||||||
|
@ -638,8 +638,7 @@
|
||||||
|
|
||||||
floatBalance = parseFloat(balance);
|
floatBalance = parseFloat(balance);
|
||||||
calculatedAmount = floatBalance * percent;
|
calculatedAmount = floatBalance * percent;
|
||||||
|
if (cid in '6, 9' && percent === 1) {
|
||||||
if (cid === '6' && percent === 1) {
|
|
||||||
amountInput.setAttribute('data-hidden', 'true');
|
amountInput.setAttribute('data-hidden', 'true');
|
||||||
amountInput.placeholder = 'Sweep All';
|
amountInput.placeholder = 'Sweep All';
|
||||||
amountInput.value = '';
|
amountInput.value = '';
|
||||||
|
@ -654,8 +653,7 @@
|
||||||
amountInput.placeholder = '';
|
amountInput.placeholder = '';
|
||||||
amountInput.disabled = false;
|
amountInput.disabled = false;
|
||||||
}
|
}
|
||||||
|
if (cid in '6, 9' && percent === 1) {
|
||||||
if (cid === '6' && percent === 1) {
|
|
||||||
var sweepAllCheckbox = document.getElementById('sweepall');
|
var sweepAllCheckbox = document.getElementById('sweepall');
|
||||||
if (sweepAllCheckbox) {
|
if (sweepAllCheckbox) {
|
||||||
sweepAllCheckbox.checked = true;
|
sweepAllCheckbox.checked = true;
|
||||||
|
@ -674,7 +672,7 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
<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 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" 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>
|
<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="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="px-6">
|
||||||
<div class="flex flex-wrap justify-end">
|
<div class="flex flex-wrap justify-end">
|
||||||
{% if w.cid != '6' %}
|
{% if w.cid not in '6, 9' %}
|
||||||
{# !XMR #}
|
{# !XMR | WOW #}
|
||||||
{% if w.show_utxo_groups %}
|
{% if w.show_utxo_groups %}
|
||||||
{% else %}
|
{% 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>
|
<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 %}
|
{% endif %} {% endif %}
|
||||||
{% if w.cid == '6' %}
|
{% if w.cid in '6, 9' %}
|
||||||
{# XMR #}
|
{# 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>
|
<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 %}
|
{% 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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -772,8 +770,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% if w.cid != '6' %}
|
{% if w.cid not in '6, 9' %}
|
||||||
{# !XMR #}
|
{# !XMR | WOW #}
|
||||||
{% if w.show_utxo_groups %}
|
{% if w.show_utxo_groups %}
|
||||||
<section class="p-6">
|
<section class="p-6">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
|
|
|
@ -213,11 +213,11 @@ const coinNameToSymbol = {
|
||||||
'Particl Blind': 'PART',
|
'Particl Blind': 'PART',
|
||||||
'Particl Anon': 'PART',
|
'Particl Anon': 'PART',
|
||||||
'Monero': 'XMR',
|
'Monero': 'XMR',
|
||||||
|
'Wownero': 'WOW',
|
||||||
'Litecoin': 'LTC',
|
'Litecoin': 'LTC',
|
||||||
'Firo': 'FIRO',
|
'Firo': 'FIRO',
|
||||||
'Dash': 'DASH',
|
'Dash': 'DASH',
|
||||||
'PIVX': 'PIVX',
|
'PIVX': 'PIVX',
|
||||||
'Wownero': 'WOW',
|
|
||||||
'Decred': 'DCR',
|
'Decred': 'DCR',
|
||||||
'Zano': 'ZANO',
|
'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'))
|
page_data['coin_from'] = getCoinType(get_data_entry(form_data, 'coin_from'))
|
||||||
coin_from = Coins(page_data['coin_from'])
|
coin_from = Coins(page_data['coin_from'])
|
||||||
ci_from = swap_client.ci(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
|
page_data['fee_from_conf'] = ci_from._conf_target # Set default value
|
||||||
parsed_data['coin_from'] = coin_from
|
parsed_data['coin_from'] = coin_from
|
||||||
except Exception:
|
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'))
|
page_data['coin_to'] = getCoinType(get_data_entry(form_data, 'coin_to'))
|
||||||
coin_to = Coins(page_data['coin_to'])
|
coin_to = Coins(page_data['coin_to'])
|
||||||
ci_to = swap_client.ci(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
|
page_data['fee_to_conf'] = ci_to._conf_target # Set default value
|
||||||
parsed_data['coin_to'] = coin_to
|
parsed_data['coin_to'] = coin_to
|
||||||
except Exception:
|
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')
|
page_data['swap_type'] = get_data_entry(form_data, 'swap_type')
|
||||||
parsed_data['swap_type'] = page_data['swap_type']
|
parsed_data['swap_type'] = page_data['swap_type']
|
||||||
swap_type = swap_type_from_string(parsed_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)
|
parsed_data['swap_type'] = strSwapType(SwapTypes.XMR_SWAP)
|
||||||
swap_type = SwapTypes.XMR_SWAP
|
swap_type = SwapTypes.XMR_SWAP
|
||||||
else:
|
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['amt_from_lock_spend_tx_fee'] = ci_from.format_amount(lock_spend_tx_fee // ci_from.COIN())
|
||||||
page_data['tla_from'] = ci_from.ticker()
|
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'):
|
if have_data_entry(form_data, 'fee_rate_to'):
|
||||||
page_data['to_fee_override'] = get_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']
|
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:
|
if 'swap_type' in parsed_data:
|
||||||
str_swap_type = parsed_data['swap_type'].lower()
|
str_swap_type = parsed_data['swap_type'].lower()
|
||||||
swap_type = swap_type_from_string(str_swap_type)
|
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
|
swap_type = SwapTypes.XMR_SWAP
|
||||||
|
|
||||||
if 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():
|
for name, c in swap_client.settings['chainclients'].items():
|
||||||
if have_data_entry(form_data, 'apply_' + name):
|
if have_data_entry(form_data, 'apply_' + name):
|
||||||
data = {'lookups': get_data_entry(form_data, 'lookups_' + 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['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['manage_daemon'] = True if get_data_entry(form_data, 'managedaemon_' + name) == 'true' else False
|
||||||
data['rpchost'] = get_data_entry(form_data, 'rpchost_' + name)
|
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'),
|
'manage_daemon': c.get('manage_daemon', 'Unknown'),
|
||||||
'connection_type': c.get('connection_type', '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]['fee_priority'] = c.get('fee_priority', 0)
|
||||||
chains_formatted[-1]['manage_wallet_daemon'] = c.get('manage_wallet_daemon', 'Unknown')
|
chains_formatted[-1]['manage_wallet_daemon'] = c.get('manage_wallet_daemon', 'Unknown')
|
||||||
chains_formatted[-1]['rpchost'] = c.get('rpchost', 'localhost')
|
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:
|
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, ):
|
if estimate_fee and coin_id not in (Coins.XMR, Coins.WOW):
|
||||||
ci = swap_client.ci(coin_id)
|
ci = swap_client.ci(coin_id)
|
||||||
ticker: str = ci.ticker()
|
ticker: str = ci.ticker()
|
||||||
err_messages.append(f'Estimate fee unavailable for {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:
|
elif coin_id == Coins.LTC:
|
||||||
txid = swap_client.withdrawLTC(type_from, value, address, subfee)
|
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))
|
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:
|
if estimate_fee:
|
||||||
fee_estimate = ci.estimateFee(value, address, sweepall)
|
fee_estimate = ci.estimateFee(value, address, sweepall)
|
||||||
suffix = 's' if fee_estimate['num_txns'] > 1 else ''
|
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['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['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')
|
wallet_data['main_address'] = w.get('main_address', 'Refresh necessary')
|
||||||
elif k == Coins.LTC:
|
elif k == Coins.LTC:
|
||||||
wallet_data['mweb_address'] = w.get('mweb_address', 'Refresh necessary')
|
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', '')
|
MONERO_VERSION_TAG = os.getenv('MONERO_VERSION_TAG', '')
|
||||||
XMR_SITE_COMMIT = 'd00169a6decd9470ebdf6a75e3351df4ebcd260a' # Lock hashes.txt to monero version
|
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 = os.getenv('PIVX_VERSION', '5.6.1')
|
||||||
PIVX_VERSION_TAG = os.getenv('PIVX_VERSION_TAG', '')
|
PIVX_VERSION_TAG = os.getenv('PIVX_VERSION_TAG', '')
|
||||||
|
|
||||||
|
@ -85,6 +89,7 @@ known_coins = {
|
||||||
'bitcoin': (BITCOIN_VERSION, BITCOIN_VERSION_TAG, ('laanwj',)),
|
'bitcoin': (BITCOIN_VERSION, BITCOIN_VERSION_TAG, ('laanwj',)),
|
||||||
'namecoin': ('0.18.0', '', ('JeremyRand',)),
|
'namecoin': ('0.18.0', '', ('JeremyRand',)),
|
||||||
'monero': (MONERO_VERSION, MONERO_VERSION_TAG, ('binaryfate',)),
|
'monero': (MONERO_VERSION, MONERO_VERSION_TAG, ('binaryfate',)),
|
||||||
|
'wownero': (WOWNERO_VERSION, WOWNERO_VERSION_TAG, ('wowario',)),
|
||||||
'pivx': (PIVX_VERSION, PIVX_VERSION_TAG, ('fuzzbawls',)),
|
'pivx': (PIVX_VERSION, PIVX_VERSION_TAG, ('fuzzbawls',)),
|
||||||
'dash': (DASH_VERSION, DASH_VERSION_TAG, ('pasta',)),
|
'dash': (DASH_VERSION, DASH_VERSION_TAG, ('pasta',)),
|
||||||
'firo': (FIRO_VERSION, FIRO_VERSION_TAG, ('reuben',)),
|
'firo': (FIRO_VERSION, FIRO_VERSION_TAG, ('reuben',)),
|
||||||
|
@ -103,6 +108,7 @@ expected_key_ids = {
|
||||||
'laanwj': ('1E4AED62986CD25D',),
|
'laanwj': ('1E4AED62986CD25D',),
|
||||||
'JeremyRand': ('2DBE339E29F6294C',),
|
'JeremyRand': ('2DBE339E29F6294C',),
|
||||||
'binaryfate': ('F0AF4D462A0BDF92',),
|
'binaryfate': ('F0AF4D462A0BDF92',),
|
||||||
|
'wowario': ('793504B449C69220',),
|
||||||
'davidburkett38': ('3620E9D387E55666',),
|
'davidburkett38': ('3620E9D387E55666',),
|
||||||
'fuzzbawls': ('C1ABA64407731FD9',),
|
'fuzzbawls': ('C1ABA64407731FD9',),
|
||||||
'pasta': ('52527BEDABE87984',),
|
'pasta': ('52527BEDABE87984',),
|
||||||
|
@ -157,6 +163,17 @@ XMR_RPC_USER = os.getenv('XMR_RPC_USER', '')
|
||||||
XMR_RPC_PWD = os.getenv('XMR_RPC_PWD', '')
|
XMR_RPC_PWD = os.getenv('XMR_RPC_PWD', '')
|
||||||
DEFAULT_XMR_RESTORE_HEIGHT = int(os.getenv('DEFAULT_XMR_RESTORE_HEIGHT', 2245107))
|
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_HOST = os.getenv('LTC_RPC_HOST', '127.0.0.1')
|
||||||
LTC_RPC_PORT = int(os.getenv('LTC_RPC_PORT', 19895))
|
LTC_RPC_PORT = int(os.getenv('LTC_RPC_PORT', 19895))
|
||||||
LTC_ONION_PORT = int(os.getenv('LTC_ONION_PORT', 9333))
|
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
|
'hide-my-port=1', # Don't share the p2p port
|
||||||
'p2p-bind-ip=127.0.0.1', # Don't broadcast ip
|
'p2p-bind-ip=127.0.0.1', # Don't broadcast ip
|
||||||
'in-peers=0', # Changes "error" in log to "incoming connections disabled"
|
'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 = [
|
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 = socket.socket
|
||||||
default_socket_timeout = socket.getdefaulttimeout()
|
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)
|
logger.info('extractCore %s v%s%s', coin, version, version_tag)
|
||||||
extract_core_overwrite = extra_opts.get('extract_core_overwrite', True)
|
extract_core_overwrite = extra_opts.get('extract_core_overwrite', True)
|
||||||
|
|
||||||
if coin in ('monero', 'firo'):
|
if coin in ('monero', 'firo', 'wownero'):
|
||||||
if coin == 'monero':
|
if coin in ('monero', 'wownero'):
|
||||||
bins = ['monerod', 'monero-wallet-rpc']
|
bins = [coin + 'd', coin + '-wallet-rpc']
|
||||||
elif coin == 'firo':
|
elif coin == 'firo':
|
||||||
bins = [coin + 'd', coin + '-cli', coin + '-tx']
|
bins = [coin + 'd', coin + '-cli', coin + '-tx']
|
||||||
else:
|
else:
|
||||||
|
@ -624,6 +657,30 @@ def prepareCore(coin, version_data, settings, data_dir, extra_opts={}):
|
||||||
assert_path = os.path.join(bin_dir, assert_filename)
|
assert_path = os.path.join(bin_dir, assert_filename)
|
||||||
if not os.path.exists(assert_path):
|
if not os.path.exists(assert_path):
|
||||||
downloadFile(assert_url, 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':
|
elif coin == 'decred':
|
||||||
arch_name = BIN_ARCH
|
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_filename = '{}-{}-{}.{}'.format(coin, version, arch_name, FILE_EXT)
|
||||||
release_page_url = 'https://github.com/decred/decred-binaries/releases/download/v{}'.format(version)
|
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_url = release_page_url + '/' + 'decred-{}-v{}.{}'.format(arch_name, version, FILE_EXT)
|
||||||
|
|
||||||
release_path = os.path.join(bin_dir, release_filename)
|
release_path = os.path.join(bin_dir, release_filename)
|
||||||
if not os.path.exists(release_path):
|
if not os.path.exists(release_path):
|
||||||
downloadFile(release_url, 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')
|
pubkeyurls.append('https://raw.githubusercontent.com/dashpay/dash/master/contrib/gitian-keys/pasta.pgp')
|
||||||
if coin == 'monero':
|
if coin == 'monero':
|
||||||
pubkeyurls.append('https://raw.githubusercontent.com/monero-project/monero/master/utils/gpg_keys/binaryfate.asc')
|
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':
|
if coin == 'firo':
|
||||||
pubkeyurls.append('https://firo.org/reuben.asc')
|
pubkeyurls.append('https://firo.org/reuben.asc')
|
||||||
|
|
||||||
if ADD_PUBKEY_URL != '':
|
if ADD_PUBKEY_URL != '':
|
||||||
pubkeyurls.append(ADD_PUBKEY_URL + '/' + pubkey_filename)
|
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:
|
with open(assert_path, 'rb') as fp:
|
||||||
verified = gpg.verify_file(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):
|
if not os.path.exists(data_dir):
|
||||||
os.makedirs(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')
|
core_conf_path = os.path.join(data_dir, coin + 'd.conf')
|
||||||
if os.path.exists(core_conf_path):
|
if os.path.exists(core_conf_path):
|
||||||
exitWithError('{} exists'.format(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('zmq-rpc-bind-ip={}\n'.format(COINS_RPCBIND_IP))
|
||||||
fp.write('prune-blockchain=1\n')
|
fp.write('prune-blockchain=1\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:
|
if tor_control_password is not None:
|
||||||
for opt_line in monerod_proxy_config:
|
for opt_line in monerod_proxy_config:
|
||||||
fp.write(opt_line + '\n')
|
fp.write(opt_line + '\n')
|
||||||
|
|
||||||
if XMR_RPC_USER != '':
|
if coin == 'wownero':
|
||||||
fp.write(f'rpc-login={XMR_RPC_USER}:{XMR_RPC_PWD}\n')
|
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)
|
wallets_dir = core_settings.get('walletsdir', data_dir)
|
||||||
if not os.path.exists(wallets_dir):
|
if not os.path.exists(wallets_dir):
|
||||||
os.makedirs(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):
|
if os.path.exists(wallet_conf_path):
|
||||||
exitWithError('{} exists'.format(wallet_conf_path))
|
exitWithError('{} exists'.format(wallet_conf_path))
|
||||||
with open(wallet_conf_path, 'w') as fp:
|
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('rpc-bind-ip={}\n'.format(COINS_RPCBIND_IP))
|
||||||
fp.write(f'wallet-dir={config_datadir}\n')
|
fp.write(f'wallet-dir={config_datadir}\n')
|
||||||
fp.write('log-file={}\n'.format(os.path.join(config_datadir, 'wallet.log')))
|
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']))
|
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':
|
if chain == 'regtest':
|
||||||
fp.write('allow-mismatched-daemon-version=1\n')
|
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]
|
coin_settings = settings['chainclients'][coin]
|
||||||
data_dir = coin_settings['datadir']
|
data_dir = coin_settings['datadir']
|
||||||
|
|
||||||
if coin == 'monero':
|
if coin in ('monero', 'wownero'):
|
||||||
core_conf_path = os.path.join(data_dir, coin + 'd.conf')
|
core_conf_path = os.path.join(data_dir, coin + 'd.conf')
|
||||||
if not os.path.exists(core_conf_path):
|
if not os.path.exists(core_conf_path):
|
||||||
exitWithError('{} does not exist'.format(core_conf_path))
|
exitWithError('{} does not exist'.format(core_conf_path))
|
||||||
|
|
||||||
wallets_dir = coin_settings.get('walletsdir', data_dir)
|
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):
|
if not os.path.exists(wallet_conf_path):
|
||||||
exitWithError('{} does not exist'.format(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
|
# Disable tor first
|
||||||
for line in fp_in:
|
for line in fp_in:
|
||||||
skip_line: bool = False
|
skip_line: bool = False
|
||||||
|
if coin == 'monero':
|
||||||
for opt_line in monerod_proxy_config:
|
for opt_line in monerod_proxy_config:
|
||||||
setting: str = opt_line[0: opt_line.find('=') + 1]
|
setting: str = opt_line[0: opt_line.find('=') + 1]
|
||||||
if line.startswith(setting):
|
if line.startswith(setting):
|
||||||
skip_line = True
|
skip_line = True
|
||||||
break
|
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:
|
if not skip_line:
|
||||||
fp.write(line)
|
fp.write(line)
|
||||||
if enable:
|
if enable:
|
||||||
|
if coin == 'monero':
|
||||||
for opt_line in monerod_proxy_config:
|
for opt_line in monerod_proxy_config:
|
||||||
fp.write(opt_line + '\n')
|
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, 'w') as fp:
|
||||||
with open(wallet_conf_path + '.last') as fp_in:
|
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('--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('--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('--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('--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('--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.')
|
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 c == Coins.XMR:
|
||||||
if coin_settings['manage_wallet_daemon']:
|
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))
|
daemons.append(startXmrWalletDaemon(coin_settings['datadir'], coin_settings['bindir'], filename))
|
||||||
elif c == Coins.DCR:
|
elif c == Coins.DCR:
|
||||||
pass
|
pass
|
||||||
|
@ -1470,6 +1561,7 @@ def main():
|
||||||
coins_changed = False
|
coins_changed = False
|
||||||
htmlhost = '127.0.0.1'
|
htmlhost = '127.0.0.1'
|
||||||
xmr_restore_height = DEFAULT_XMR_RESTORE_HEIGHT
|
xmr_restore_height = DEFAULT_XMR_RESTORE_HEIGHT
|
||||||
|
wow_restore_height = DEFAULT_WOW_RESTORE_HEIGHT
|
||||||
prepare_bin_only = False
|
prepare_bin_only = False
|
||||||
no_cores = False
|
no_cores = False
|
||||||
enable_tor = False
|
enable_tor = False
|
||||||
|
@ -1585,6 +1677,9 @@ def main():
|
||||||
if name == 'xmrrestoreheight':
|
if name == 'xmrrestoreheight':
|
||||||
xmr_restore_height = int(s[1])
|
xmr_restore_height = int(s[1])
|
||||||
continue
|
continue
|
||||||
|
if name == 'wowrestoreheight':
|
||||||
|
wow_restore_height = int(s[1])
|
||||||
|
continue
|
||||||
if name == 'keysdirpath':
|
if name == 'keysdirpath':
|
||||||
extra_opts['keysdirpath'] = os.path.expanduser(s[1].strip('"'))
|
extra_opts['keysdirpath'] = os.path.expanduser(s[1].strip('"'))
|
||||||
continue
|
continue
|
||||||
|
@ -1825,6 +1920,28 @@ def main():
|
||||||
'core_version_group': 18,
|
'core_version_group': 18,
|
||||||
'chain_lookups': 'local',
|
'chain_lookups': 'local',
|
||||||
'startup_tries': 40,
|
'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 != '':
|
if XMR_RPC_USER != '':
|
||||||
chainclients['monero']['rpcuser'] = XMR_RPC_USER
|
chainclients['monero']['rpcuser'] = XMR_RPC_USER
|
||||||
chainclients['monero']['rpcpassword'] = XMR_RPC_PWD
|
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 != '':
|
if PIVX_RPC_USER != '':
|
||||||
chainclients['pivx']['rpcuser'] = PIVX_RPC_USER
|
chainclients['pivx']['rpcuser'] = PIVX_RPC_USER
|
||||||
chainclients['pivx']['rpcpassword'] = PIVX_RPC_PWD
|
chainclients['pivx']['rpcpassword'] = PIVX_RPC_PWD
|
||||||
|
@ -1854,6 +1974,7 @@ def main():
|
||||||
chainclients['nav']['rpcpassword'] = NAV_RPC_PWD
|
chainclients['nav']['rpcpassword'] = NAV_RPC_PWD
|
||||||
|
|
||||||
chainclients['monero']['walletsdir'] = os.getenv('XMR_WALLETS_DIR', chainclients['monero']['datadir'])
|
chainclients['monero']['walletsdir'] = os.getenv('XMR_WALLETS_DIR', chainclients['monero']['datadir'])
|
||||||
|
chainclients['wownero']['walletsdir'] = os.getenv('WOW_WALLETS_DIR', chainclients['wownero']['datadir'])
|
||||||
|
|
||||||
if initwalletsonly:
|
if initwalletsonly:
|
||||||
logger.info('Initialising wallets')
|
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=[]):
|
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)
|
datadir_path = os.path.expanduser(node_dir)
|
||||||
args = [daemon_bin, '--non-interactive', '--config-file=' + os.path.join(datadir_path, 'monerod.conf')] + opts
|
args = [daemon_path, '--non-interactive', '--config-file=' + os.path.join(datadir_path, daemon_bin + '.conf')] + opts
|
||||||
logging.info('Starting node {} --data-dir={}'.format(daemon_bin, node_dir))
|
logging.info('Starting node {} --data-dir={}'.format(daemon_path, node_dir))
|
||||||
|
|
||||||
# return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
# 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_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
|
args += opts
|
||||||
|
|
||||||
if needs_rewrite:
|
if needs_rewrite:
|
||||||
logging.info('Rewriting monero_wallet.conf')
|
logging.info('Rewriting wallet config')
|
||||||
shutil.copyfile(config_path, config_path + '.last')
|
shutil.copyfile(config_path, config_path + '.last')
|
||||||
with open(config_path + '.last') as fp_from, open(config_path, 'w') as fp_to:
|
with open(config_path + '.last') as fp_from, open(config_path, 'w') as fp_to:
|
||||||
for line in fp_from:
|
for line in fp_from:
|
||||||
|
@ -208,10 +208,10 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning('Not starting unknown coin: {}'.format(c))
|
logger.warning('Not starting unknown coin: {}'.format(c))
|
||||||
continue
|
continue
|
||||||
if c == 'monero':
|
if c in ('monero', 'wownero'):
|
||||||
if v['manage_daemon'] is True:
|
if v['manage_daemon'] is True:
|
||||||
swap_client.log.info(f'Starting {display_name} daemon')
|
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))
|
daemons.append(startXmrDaemon(v['datadir'], v['bindir'], filename))
|
||||||
pid = daemons[-1].handle.pid
|
pid = daemons[-1].handle.pid
|
||||||
swap_client.log.info('Started {} {}'.format(filename, 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'])
|
proxy_host, proxy_port = swap_client.getXMRWalletProxy(coin_id, v['rpchost'])
|
||||||
if proxy_host:
|
if proxy_host:
|
||||||
proxy_log_str = ' through proxy'
|
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))
|
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(daemon_rpcuser + ':' + daemon_rpcpass)
|
||||||
|
|
||||||
opts.append('--trusted-daemon' if trusted_daemon else '--untrusted-daemon')
|
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))
|
daemons.append(startXmrWalletDaemon(v['datadir'], v['bindir'], filename, opts))
|
||||||
pid = daemons[-1].handle.pid
|
pid = daemons[-1].handle.pid
|
||||||
swap_client.log.info('Started {} {}'.format(filename, 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}/swapclient:/data/swapclient
|
||||||
- ${DATA_PATH}/monero_daemon:/data/monero_daemon
|
- ${DATA_PATH}/monero_daemon:/data/monero_daemon
|
||||||
- ${DATA_PATH}/monero_wallet:/data/monero_wallet
|
- ${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}/particl:/data/particl
|
||||||
- ${DATA_PATH}/bitcoin:/data/bitcoin
|
- ${DATA_PATH}/bitcoin:/data/bitcoin
|
||||||
- ${DATA_PATH}/litecoin:/data/litecoin
|
- ${DATA_PATH}/litecoin:/data/litecoin
|
||||||
|
@ -45,6 +47,16 @@
|
||||||
- XMR_WALLET_RPC_USER
|
- XMR_WALLET_RPC_USER
|
||||||
- XMR_WALLET_RPC_PWD
|
- XMR_WALLET_RPC_PWD
|
||||||
- DEFAULT_XMR_RESTORE_HEIGHT
|
- 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_DATA_DIR
|
||||||
- PIVX_RPC_HOST
|
- PIVX_RPC_HOST
|
||||||
- PIVX_RPC_PORT
|
- PIVX_RPC_PORT
|
||||||
|
|
|
@ -44,6 +44,16 @@ BASE_XMR_WALLET_PORT=29998
|
||||||
XMR_WALLET_RPC_USER=xmr_wallet_user
|
XMR_WALLET_RPC_USER=xmr_wallet_user
|
||||||
XMR_WALLET_RPC_PWD=xmr_wallet_pwd
|
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_DATA_DIR=/data/pivx
|
||||||
PIVX_RPC_HOST=pivx_core
|
PIVX_RPC_HOST=pivx_core
|
||||||
PIVX_RPC_PORT=51473
|
PIVX_RPC_PORT=51473
|
||||||
|
|
|
@ -57,12 +57,12 @@ def main():
|
||||||
if coin_name == 'particl':
|
if coin_name == 'particl':
|
||||||
# Nothing to do
|
# Nothing to do
|
||||||
continue
|
continue
|
||||||
if coin_name == 'monero':
|
if coin_name in ('monero', 'wownero'):
|
||||||
with open(os.path.join(fragments_dir, '1_monero-wallet.yml'), 'rb') as fp_in:
|
with open(os.path.join(fragments_dir, '1_{coin_name}-wallet.yml'), 'rb') as fp_in:
|
||||||
for line in fp_in:
|
for line in fp_in:
|
||||||
fp.write(line)
|
fp.write(line)
|
||||||
fpp.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:
|
for line in fp_in:
|
||||||
fp.write(line)
|
fp.write(line)
|
||||||
continue
|
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",
|
"particl",
|
||||||
"bitcoin",
|
"bitcoin",
|
||||||
"monero",
|
"monero",
|
||||||
|
"wownero",
|
||||||
],
|
],
|
||||||
install_requires=[
|
install_requires=[
|
||||||
"wheel",
|
"wheel",
|
||||||
|
|
1
tox.ini
1
tox.ini
|
@ -11,6 +11,7 @@ passenv =
|
||||||
BITCOIN_BINDIR
|
BITCOIN_BINDIR
|
||||||
LITECOIN_BINDIR
|
LITECOIN_BINDIR
|
||||||
XMR_BINDIR
|
XMR_BINDIR
|
||||||
|
WOW_BINDIR
|
||||||
TEST_PREPARE_PATH
|
TEST_PREPARE_PATH
|
||||||
TEST_RELOAD_PATH
|
TEST_RELOAD_PATH
|
||||||
deps =
|
deps =
|
||||||
|
|
Loading…
Reference in a new issue