mirror of
https://github.com/basicswap/basicswap.git
synced 2024-12-22 11:39:34 +00:00
Integrate Decred with wallet encryption.
dcrwallet requires the password to be entered at the first startup when encrypted. basicswap-run with --startonlycoin=decred and the WALLET_ENCRYPTION_PWD environment var set can be used for the initial sync.
This commit is contained in:
parent
3e16ea487c
commit
247f23cb4a
7 changed files with 102 additions and 10 deletions
|
@ -6861,6 +6861,12 @@ class BasicSwap(BaseApp):
|
||||||
self.ci(coin).setAnonTxRingSize(new_anon_tx_ring_size)
|
self.ci(coin).setAnonTxRingSize(new_anon_tx_ring_size)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if 'wallet_pwd' in data:
|
||||||
|
new_wallet_pwd = data['wallet_pwd']
|
||||||
|
if settings_cc.get('wallet_pwd', '') != new_wallet_pwd:
|
||||||
|
settings_changed = True
|
||||||
|
settings_cc['wallet_pwd'] = new_wallet_pwd
|
||||||
|
|
||||||
if settings_changed:
|
if settings_changed:
|
||||||
settings_path = os.path.join(self.data_dir, cfg.CONFIG_FILENAME)
|
settings_path = os.path.join(self.data_dir, cfg.CONFIG_FILENAME)
|
||||||
settings_path_new = settings_path + '.new'
|
settings_path_new = settings_path + '.new'
|
||||||
|
|
|
@ -1379,7 +1379,7 @@ class BTCInterface(Secp256k1Interface):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def isWalletEncryptedLocked(self):
|
def isWalletEncryptedLocked(self) -> (bool, bool):
|
||||||
wallet_info = self.rpc_wallet('getwalletinfo')
|
wallet_info = self.rpc_wallet('getwalletinfo')
|
||||||
encrypted = 'unlocked_until' in wallet_info
|
encrypted = 'unlocked_until' in wallet_info
|
||||||
locked = encrypted and wallet_info['unlocked_until'] <= 0
|
locked = encrypted and wallet_info['unlocked_until'] <= 0
|
||||||
|
|
|
@ -320,6 +320,44 @@ class DCRInterface(Secp256k1Interface):
|
||||||
# Load with --create
|
# Load with --create
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def isWalletEncrypted(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def isWalletLocked(self) -> bool:
|
||||||
|
walletislocked = self.rpc_wallet('walletislocked')
|
||||||
|
return walletislocked
|
||||||
|
|
||||||
|
def isWalletEncryptedLocked(self) -> (bool, bool):
|
||||||
|
walletislocked = self.rpc_wallet('walletislocked')
|
||||||
|
return True, walletislocked
|
||||||
|
|
||||||
|
def changeWalletPassword(self, old_password: str, new_password: str):
|
||||||
|
self._log.info('changeWalletPassword - {}'.format(self.ticker()))
|
||||||
|
if old_password == '':
|
||||||
|
# Read initial pwd from settings
|
||||||
|
settings = self._sc.getChainClientSettings(self.coin_type())
|
||||||
|
old_password = settings['wallet_pwd']
|
||||||
|
self.rpc_wallet('walletpassphrasechange', [old_password, new_password])
|
||||||
|
|
||||||
|
# Lock wallet to match other coins
|
||||||
|
self.rpc_wallet('walletlock')
|
||||||
|
|
||||||
|
# Clear initial password
|
||||||
|
self._sc.editSettings(self.coin_name().lower(), {'wallet_pwd': ''})
|
||||||
|
|
||||||
|
def unlockWallet(self, password: str):
|
||||||
|
if password == '':
|
||||||
|
return
|
||||||
|
self._log.info('unlockWallet - {}'.format(self.ticker()))
|
||||||
|
|
||||||
|
# Max timeout value, ~3 years
|
||||||
|
self.rpc_wallet('walletpassphrase', [password, 100000000])
|
||||||
|
self._sc.checkWalletSeed(self.coin_type())
|
||||||
|
|
||||||
|
def lockWallet(self):
|
||||||
|
self._log.info('lockWallet - {}'.format(self.ticker()))
|
||||||
|
self.rpc_wallet('walletlock')
|
||||||
|
|
||||||
def getWalletSeedID(self):
|
def getWalletSeedID(self):
|
||||||
masterpubkey = self.rpc_wallet('getmasterpubkey')
|
masterpubkey = self.rpc_wallet('getmasterpubkey')
|
||||||
masterpubkey_data = self.decode_address(masterpubkey)[4:]
|
masterpubkey_data = self.decode_address(masterpubkey)[4:]
|
||||||
|
|
|
@ -1311,8 +1311,10 @@ def initialise_wallets(particl_wallet_mnemonic, with_coins, data_dir, settings,
|
||||||
if c == Coins.DCR:
|
if c == Coins.DCR:
|
||||||
if coin_settings['manage_wallet_daemon']:
|
if coin_settings['manage_wallet_daemon']:
|
||||||
from basicswap.interface.dcr.util import createDCRWallet
|
from basicswap.interface.dcr.util import createDCRWallet
|
||||||
|
|
||||||
|
dcr_password = coin_settings['wallet_pwd'] if WALLET_ENCRYPTION_PWD == '' else WALLET_ENCRYPTION_PWD
|
||||||
extra_opts = ['--appdata="{}"'.format(coin_settings['datadir']),
|
extra_opts = ['--appdata="{}"'.format(coin_settings['datadir']),
|
||||||
'--pass={}'.format(coin_settings['wallet_pwd']),
|
'--pass={}'.format(dcr_password),
|
||||||
]
|
]
|
||||||
|
|
||||||
filename = 'dcrwallet' + ('.exe' if os.name == 'nt' else '')
|
filename = 'dcrwallet' + ('.exe' if os.name == 'nt' else '')
|
||||||
|
@ -1381,6 +1383,9 @@ def initialise_wallets(particl_wallet_mnemonic, with_coins, data_dir, settings,
|
||||||
else:
|
else:
|
||||||
print(f'WARNING - Failed to initialise wallet for {getCoinName(c)}: {e}')
|
print(f'WARNING - Failed to initialise wallet for {getCoinName(c)}: {e}')
|
||||||
|
|
||||||
|
if 'decred' in with_coins and WALLET_ENCRYPTION_PWD != '':
|
||||||
|
print('WARNING - dcrwallet requires the password to be entered at the first startup when encrypted.\nPlease use basicswap-run with --startonlycoin=decred and the WALLET_ENCRYPTION_PWD environment var set for the initial sync.')
|
||||||
|
|
||||||
if particl_wallet_mnemonic is not None:
|
if particl_wallet_mnemonic is not None:
|
||||||
if particl_wallet_mnemonic:
|
if particl_wallet_mnemonic:
|
||||||
# Print directly to stdout for tests
|
# Print directly to stdout for tests
|
||||||
|
@ -1693,7 +1698,7 @@ def main():
|
||||||
'connection_type': 'rpc' if 'decred' in with_coins else 'none',
|
'connection_type': 'rpc' if 'decred' in with_coins else 'none',
|
||||||
'manage_daemon': True if ('decred' in with_coins and DCR_RPC_HOST == '127.0.0.1') else False,
|
'manage_daemon': True if ('decred' in with_coins and DCR_RPC_HOST == '127.0.0.1') else False,
|
||||||
'manage_wallet_daemon': True if ('decred' in with_coins and DCR_WALLET_RPC_HOST == '127.0.0.1') else False,
|
'manage_wallet_daemon': True if ('decred' in with_coins and DCR_WALLET_RPC_HOST == '127.0.0.1') else False,
|
||||||
'wallet_pwd': DCR_WALLET_PWD,
|
'wallet_pwd': DCR_WALLET_PWD if WALLET_ENCRYPTION_PWD == '' else '',
|
||||||
'rpchost': DCR_RPC_HOST,
|
'rpchost': DCR_RPC_HOST,
|
||||||
'rpcport': DCR_RPC_PORT + port_offset,
|
'rpcport': DCR_RPC_PORT + port_offset,
|
||||||
'walletrpchost': DCR_WALLET_RPC_HOST,
|
'walletrpchost': DCR_WALLET_RPC_HOST,
|
||||||
|
|
|
@ -40,7 +40,7 @@ class Daemon:
|
||||||
|
|
||||||
|
|
||||||
def is_known_coin(coin_name: str) -> bool:
|
def is_known_coin(coin_name: str) -> bool:
|
||||||
for k, v in chainparams:
|
for k, v in chainparams.items():
|
||||||
if coin_name == v['name']:
|
if coin_name == v['name']:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -169,7 +169,11 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
||||||
pids_path = os.path.join(data_dir, '.pids')
|
pids_path = os.path.join(data_dir, '.pids')
|
||||||
|
|
||||||
if os.getenv('WALLET_ENCRYPTION_PWD', '') != '':
|
if os.getenv('WALLET_ENCRYPTION_PWD', '') != '':
|
||||||
raise ValueError('Please unset the WALLET_ENCRYPTION_PWD environment variable.')
|
if 'decred' in start_only_coins:
|
||||||
|
# Workaround for dcrwallet requiring password for initial startup
|
||||||
|
logger.warning('Allowing set WALLET_ENCRYPTION_PWD var with --startonlycoin=decred.')
|
||||||
|
else:
|
||||||
|
raise ValueError('Please unset the WALLET_ENCRYPTION_PWD environment variable.')
|
||||||
|
|
||||||
if not os.path.exists(settings_path):
|
if not os.path.exists(settings_path):
|
||||||
raise ValueError('Settings file not found: ' + str(settings_path))
|
raise ValueError('Settings file not found: ' + str(settings_path))
|
||||||
|
@ -255,7 +259,11 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
||||||
filename = 'dcrwallet' + ('.exe' if os.name == 'nt' else '')
|
filename = 'dcrwallet' + ('.exe' if os.name == 'nt' else '')
|
||||||
|
|
||||||
wallet_pwd = v['wallet_pwd']
|
wallet_pwd = v['wallet_pwd']
|
||||||
extra_opts.append(f'--pass="{wallet_pwd}"')
|
if wallet_pwd == '':
|
||||||
|
# Only set when in startonlycoin mode
|
||||||
|
wallet_pwd = os.getenv('WALLET_ENCRYPTION_PWD', '')
|
||||||
|
if wallet_pwd != '':
|
||||||
|
extra_opts.append(f'--pass="{wallet_pwd}"')
|
||||||
extra_config = {'add_datadir': False, 'stdout_to_file': True, 'stdout_filename': 'dcrwallet_stdout.log'}
|
extra_config = {'add_datadir': False, 'stdout_to_file': True, 'stdout_filename': 'dcrwallet_stdout.log'}
|
||||||
daemons.append(startDaemon(appdata, v['bindir'], filename, opts=extra_opts, extra_config=extra_config))
|
daemons.append(startDaemon(appdata, v['bindir'], filename, opts=extra_opts, extra_config=extra_config))
|
||||||
pid = daemons[-1].handle.pid
|
pid = daemons[-1].handle.pid
|
||||||
|
|
|
@ -594,6 +594,7 @@ class Test(BaseTest):
|
||||||
'rpcpassword': 'test_pass' + str(node_id),
|
'rpcpassword': 'test_pass' + str(node_id),
|
||||||
'datadir': os.path.join(datadir, 'dcr_' + str(node_id)),
|
'datadir': os.path.join(datadir, 'dcr_' + str(node_id)),
|
||||||
'bindir': DCR_BINDIR,
|
'bindir': DCR_BINDIR,
|
||||||
|
'wallet_pwd': 'test_pass',
|
||||||
'use_csv': True,
|
'use_csv': True,
|
||||||
'use_segwit': True,
|
'use_segwit': True,
|
||||||
'blocks_confirmed': 1,
|
'blocks_confirmed': 1,
|
||||||
|
@ -942,8 +943,41 @@ class Test(BaseTest):
|
||||||
amount_proved = ci0.verifyProofOfFunds(funds_proof[0], funds_proof[1], funds_proof[2], 'test'.encode('utf-8'))
|
amount_proved = ci0.verifyProofOfFunds(funds_proof[0], funds_proof[1], funds_proof[2], 'test'.encode('utf-8'))
|
||||||
assert (amount_proved >= require_amount)
|
assert (amount_proved >= require_amount)
|
||||||
|
|
||||||
|
def test_009_wallet_encryption(self):
|
||||||
|
logging.info('---------- Test {} wallet encryption'.format(self.test_coin.name))
|
||||||
|
|
||||||
|
for coin in ('part', 'dcr', 'xmr'):
|
||||||
|
jsw = read_json_api(1800, f'wallets/{coin}')
|
||||||
|
assert (jsw['encrypted'] is (True if coin == 'dcr' else False))
|
||||||
|
assert (jsw['locked'] is False)
|
||||||
|
|
||||||
|
read_json_api(1800, 'setpassword', {'oldpassword': '', 'newpassword': 'notapassword123'})
|
||||||
|
|
||||||
|
# Entire system is locked with Particl wallet
|
||||||
|
jsw = read_json_api(1800, 'wallets/dcr')
|
||||||
|
assert ('Coin must be unlocked' in jsw['error'])
|
||||||
|
|
||||||
|
read_json_api(1800, 'unlock', {'coin': 'part', 'password': 'notapassword123'})
|
||||||
|
|
||||||
|
for coin in ('dcr', 'xmr'):
|
||||||
|
jsw = read_json_api(1800, f'wallets/{coin}')
|
||||||
|
assert (jsw['encrypted'] is True)
|
||||||
|
assert (jsw['locked'] is True)
|
||||||
|
|
||||||
|
read_json_api(1800, 'lock', {'coin': 'part'})
|
||||||
|
jsw = read_json_api(1800, 'wallets/part')
|
||||||
|
assert ('Coin must be unlocked' in jsw['error'])
|
||||||
|
|
||||||
|
read_json_api(1800, 'setpassword', {'oldpassword': 'notapassword123', 'newpassword': 'notapassword456'})
|
||||||
|
read_json_api(1800, 'unlock', {'password': 'notapassword456'})
|
||||||
|
|
||||||
|
for coin in ('part', 'dcr', 'xmr'):
|
||||||
|
jsw = read_json_api(1800, f'wallets/{coin}')
|
||||||
|
assert (jsw['encrypted'] is True)
|
||||||
|
assert (jsw['locked'] is False)
|
||||||
|
|
||||||
def test_010_txn_size(self):
|
def test_010_txn_size(self):
|
||||||
logging.info('---------- Test {} txn_size'.format(self.test_coin.name))
|
logging.info('---------- Test {} txn size'.format(self.test_coin.name))
|
||||||
|
|
||||||
swap_clients = self.swap_clients
|
swap_clients = self.swap_clients
|
||||||
ci = swap_clients[0].ci(self.test_coin)
|
ci = swap_clients[0].ci(self.test_coin)
|
||||||
|
|
|
@ -250,9 +250,10 @@ class Test(unittest.TestCase):
|
||||||
self.update_thread_dcr = threading.Thread(target=updateThreadDCR, args=(self,))
|
self.update_thread_dcr = threading.Thread(target=updateThreadDCR, args=(self,))
|
||||||
self.update_thread_dcr.start()
|
self.update_thread_dcr.start()
|
||||||
|
|
||||||
# Lower output split threshold for more stakeable outputs
|
if RESET_TEST:
|
||||||
for i in range(NUM_NODES):
|
# Lower output split threshold for more stakeable outputs
|
||||||
callpartrpc(i, 'walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}])
|
for i in range(NUM_NODES):
|
||||||
|
callpartrpc(i, 'walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}])
|
||||||
self.update_thread = threading.Thread(target=updateThread, args=(self,))
|
self.update_thread = threading.Thread(target=updateThread, args=(self,))
|
||||||
self.update_thread.start()
|
self.update_thread.start()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue