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
							
								
									fcf234ef34
								
							
						
					
					
						commit
						76445146fb
					
				@ -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,6 +169,10 @@ 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', '') != '':
 | 
				
			||||||
 | 
					        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.')
 | 
					            raise ValueError('Please unset the WALLET_ENCRYPTION_PWD environment variable.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if not os.path.exists(settings_path):
 | 
					    if not os.path.exists(settings_path):
 | 
				
			||||||
@ -255,6 +259,10 @@ 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']
 | 
				
			||||||
 | 
					                    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_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))
 | 
				
			||||||
 | 
				
			|||||||
@ -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,6 +250,7 @@ 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()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if RESET_TEST:
 | 
				
			||||||
            # Lower output split threshold for more stakeable outputs
 | 
					            # Lower output split threshold for more stakeable outputs
 | 
				
			||||||
            for i in range(NUM_NODES):
 | 
					            for i in range(NUM_NODES):
 | 
				
			||||||
                callpartrpc(i, 'walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}])
 | 
					                callpartrpc(i, 'walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}])
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user