def __init__(self): keystore_dir = self.get_keystore_path() self.account_utils = AccountUtils(keystore_dir=keystore_dir) self._pyetheroll = None # per address cached merged logs, used to compare with next pulls self.merged_logs = {} self.last_roll_activity = None
def __init__(self, keystore_dir=None, chain_id=ChainID.MAINNET): if keystore_dir is None: keystore_dir = PyWalib.get_default_keystore_path() self.keystore_dir = keystore_dir self.account_utils = AccountUtils(keystore_dir=self.keystore_dir) self.chain_id = chain_id self.provider = HTTPProviderFactory.create(self.chain_id) self.web3 = Web3(self.provider)
def test_get_or_create(self): """ Checks if the singleton is handled properly. It should only be different when changing keystore_dir. """ account_utils = AccountUtils.get_or_create(self.keystore_dir) assert account_utils == AccountUtils.get_or_create(self.keystore_dir) with TemporaryDirectory() as keystore_dir: assert account_utils != AccountUtils.get_or_create(keystore_dir) assert AccountUtils.get_or_create(keystore_dir) == \ AccountUtils.get_or_create(keystore_dir)
def test_pyethapp(self): from pyethapp.accounts import Account from ethereum_utils import AccountUtils AccountUtils.patch_ethereum_tools_keys() password = "******" uuid = None account = Account.new(password, uuid=uuid) # restore iterations address = account.address.hex() self.assertIsNotNone(account) self.assertIsNotNone(address)
def __init__(self, osc_server_port=None): """ Set `osc_server_port` to enable UI synchronization with service. """ keystore_dir = self.get_keystore_path() self.account_utils = AccountUtils(keystore_dir=keystore_dir) self._pyetheroll = None # per address cached merged logs, used to compare with next pulls self.merged_logs = {} self.last_roll_activity = None self.osc_app_client = None if osc_server_port is not None: self.osc_app_client = OscAppClient('localhost', osc_server_port)
def account_utils(self): """ Gets or creates the AccountUtils object so it loads lazily. """ from ethereum_utils import AccountUtils keystore_dir = Settings.get_keystore_path() return AccountUtils.get_or_create(keystore_dir)
def account_utils(self): """ Gets or creates the AccountUtils object so it loads lazily. """ from ethereum_utils import AccountUtils if self._account_utils is None: keystore_dir = self.get_keystore_path() self._account_utils = AccountUtils(keystore_dir=keystore_dir) return self._account_utils
def test_delete_account(self): """ Creates a new account and delete it. Then verify we can load the account from the backup/trash location. """ password = PASSWORD account = self.account_utils.new_account(password, iterations=1) address = account.address self.assertEqual(len(self.account_utils.get_account_list()), 1) # deletes the account and verifies it's not loaded anymore self.account_utils.delete_account(account) self.assertEqual(len(self.account_utils.get_account_list()), 0) # even recreating the AccountUtils object self.account_utils = AccountUtils(self.keystore_dir) self.assertEqual(len(self.account_utils.get_account_list()), 0) # tries to reload it from the backup/trash location deleted_keystore_dir = AccountUtils.deleted_account_dir( self.keystore_dir) self.account_utils = AccountUtils(deleted_keystore_dir) self.assertEqual(len(self.account_utils.get_account_list()), 1) self.assertEqual(self.account_utils.get_account_list()[0].address, address)
def test_deleted_account_dir(self): """ The deleted_account_dir() helper method should be working with and without trailing slash. """ expected_deleted_keystore_dir = '/tmp/keystore-deleted' keystore_dirs = [ # without trailing slash '/tmp/keystore', # with one trailing slash '/tmp/keystore/', # with two trailing slashes '/tmp/keystore//', ] for keystore_dir in keystore_dirs: self.assertEqual(AccountUtils.deleted_account_dir(keystore_dir), expected_deleted_keystore_dir)
def test_get_account_list_no_dir(self): """ The keystore directory should be created if it doesn't exist, refs: https://github.com/AndreMiras/PyWallet/issues/133 """ # nominal case when the directory already exists with TemporaryDirectory() as keystore_dir: self.assertTrue(os.path.isdir(keystore_dir)) account_utils = AccountUtils(keystore_dir) self.assertEqual(account_utils.get_account_list(), []) # when the directory doesn't exist it should also be created self.assertFalse(os.path.isdir(keystore_dir)) account_utils = AccountUtils(keystore_dir) self.assertTrue(os.path.isdir(keystore_dir)) self.assertEqual(account_utils.get_account_list(), []) shutil.rmtree(keystore_dir, ignore_errors=True)
def player_roll_dice(self, bet_size_ether, chances, wallet_path, wallet_password, gas_price_gwei=DEFAULT_GAS_PRICE_GWEI): """ Signs and broadcasts `playerRollDice` transaction. Returns transaction hash. """ roll_under = chances # `w3.toWei` one has some issues on Android, see: # https://github.com/AndreMiras/EtherollApp/issues/77 # value_wei = w3.toWei(bet_size_ether, 'ether') value_wei = int(bet_size_ether * 1e18) gas = 310000 gas_price = w3.toWei(gas_price_gwei, 'gwei') # since Account.load is hanging while decrypting the password # we set password to None and use `w3.eth.account.decrypt` instead account = Account.load(wallet_path, password=None) from_address_normalized = checksum_encode(account.address) nonce = self.web3.eth.getTransactionCount(from_address_normalized) transaction = { 'chainId': self.chain_id.value, 'gas': gas, 'gasPrice': gas_price, 'nonce': nonce, 'value': value_wei, } transaction = self.contract.functions.playerRollDice( roll_under).buildTransaction(transaction) private_key = AccountUtils.get_private_key(wallet_path, wallet_password) signed_tx = self.web3.eth.account.signTransaction( transaction, private_key) tx_hash = self.web3.eth.sendRawTransaction(signed_tx.rawTransaction) return tx_hash
def test_delete_account_already_exists(self): """ If the destination (backup/trash) directory where the account is moved already exists, it should be handled gracefully. This could happens if the account gets deleted, then reimported and deleted again, refs: https://github.com/AndreMiras/PyWallet/issues/88 """ password = PASSWORD account = self.account_utils.new_account(password, iterations=1) # creates a file in the backup/trash folder that would conflict # with the deleted account deleted_keystore_dir = AccountUtils.deleted_account_dir( self.keystore_dir) os.makedirs(deleted_keystore_dir) account_filename = os.path.basename(account.path) deleted_account_path = os.path.join(deleted_keystore_dir, account_filename) # create that file open(deleted_account_path, 'a').close() # then deletes the account and verifies it worked self.assertEqual(len(self.account_utils.get_account_list()), 1) self.account_utils.delete_account(account) self.assertEqual(len(self.account_utils.get_account_list()), 0)
class TestAccountUtils(unittest.TestCase): def setUp(self): self.keystore_dir = mkdtemp() self.account_utils = AccountUtils(self.keystore_dir) def tearDown(self): shutil.rmtree(self.keystore_dir, ignore_errors=True) def test_new_account(self): """ Simple account creation test case. 1) verifies the current account list is empty 2) creates a new account and verify we can retrieve it 3) tries to unlock the account """ # 1) verifies the current account list is empty self.assertEqual(self.account_utils.get_account_list(), []) # 2) creates a new account and verify we can retrieve it password = PASSWORD account = self.account_utils.new_account(password, iterations=1) self.assertEqual(len(self.account_utils.get_account_list()), 1) self.assertEqual(account, self.account_utils.get_account_list()[0]) # 3) tries to unlock the account # it's unlocked by default after creation self.assertFalse(account.locked) # let's lock it and unlock it back account.lock() self.assertTrue(account.locked) account.unlock(password) self.assertFalse(account.locked) def test_get_account_list(self): """ Makes sure get_account_list() loads properly accounts from file system. """ password = PASSWORD self.assertEqual(self.account_utils.get_account_list(), []) account = self.account_utils.new_account(password, iterations=1) self.assertEqual(len(self.account_utils.get_account_list()), 1) account = self.account_utils.get_account_list()[0] self.assertIsNotNone(account.path) # removes the cache copy and checks again if it gets loaded self.account_utils._accounts = None self.assertEqual(len(self.account_utils.get_account_list()), 1) account = self.account_utils.get_account_list()[0] self.assertIsNotNone(account.path) def test_get_account_list_error(self): """ get_account_list() should not cache empty account on PermissionError. """ # creates a temporary account that we'll try to list password = PASSWORD account = Account.new(password, uuid=None, iterations=1) account.path = os.path.join(self.keystore_dir, account.address.hex()) with open(account.path, 'w') as f: f.write(account.dump()) # `listdir()` can raise a `PermissionError` with mock.patch('os.listdir') as mock_listdir: mock_listdir.side_effect = PermissionError with self.assertRaises(PermissionError): self.account_utils.get_account_list() # the empty account list should not be catched and loading it again # should show the existing account on file system self.assertEqual(len(self.account_utils.get_account_list()), 1) self.assertEqual(self.account_utils.get_account_list()[0].address, account.address) def test_get_account_list_no_dir(self): """ The keystore directory should be created if it doesn't exist, refs: https://github.com/AndreMiras/PyWallet/issues/133 """ # nominal case when the directory already exists with TemporaryDirectory() as keystore_dir: self.assertTrue(os.path.isdir(keystore_dir)) account_utils = AccountUtils(keystore_dir) self.assertEqual(account_utils.get_account_list(), []) # when the directory doesn't exist it should also be created self.assertFalse(os.path.isdir(keystore_dir)) account_utils = AccountUtils(keystore_dir) self.assertTrue(os.path.isdir(keystore_dir)) self.assertEqual(account_utils.get_account_list(), []) shutil.rmtree(keystore_dir, ignore_errors=True) def test_deleted_account_dir(self): """ The deleted_account_dir() helper method should be working with and without trailing slash. """ expected_deleted_keystore_dir = '/tmp/keystore-deleted' keystore_dirs = [ # without trailing slash '/tmp/keystore', # with one trailing slash '/tmp/keystore/', # with two trailing slashes '/tmp/keystore//', ] for keystore_dir in keystore_dirs: self.assertEqual(AccountUtils.deleted_account_dir(keystore_dir), expected_deleted_keystore_dir) def test_delete_account(self): """ Creates a new account and delete it. Then verify we can load the account from the backup/trash location. """ password = PASSWORD account = self.account_utils.new_account(password, iterations=1) address = account.address self.assertEqual(len(self.account_utils.get_account_list()), 1) # deletes the account and verifies it's not loaded anymore self.account_utils.delete_account(account) self.assertEqual(len(self.account_utils.get_account_list()), 0) # even recreating the AccountUtils object self.account_utils = AccountUtils(self.keystore_dir) self.assertEqual(len(self.account_utils.get_account_list()), 0) # tries to reload it from the backup/trash location deleted_keystore_dir = AccountUtils.deleted_account_dir( self.keystore_dir) self.account_utils = AccountUtils(deleted_keystore_dir) self.assertEqual(len(self.account_utils.get_account_list()), 1) self.assertEqual(self.account_utils.get_account_list()[0].address, address) def test_delete_account_already_exists(self): """ If the destination (backup/trash) directory where the account is moved already exists, it should be handled gracefully. This could happens if the account gets deleted, then reimported and deleted again, refs: https://github.com/AndreMiras/PyWallet/issues/88 """ password = PASSWORD account = self.account_utils.new_account(password, iterations=1) # creates a file in the backup/trash folder that would conflict # with the deleted account deleted_keystore_dir = AccountUtils.deleted_account_dir( self.keystore_dir) os.makedirs(deleted_keystore_dir) account_filename = os.path.basename(account.path) deleted_account_path = os.path.join(deleted_keystore_dir, account_filename) # create that file open(deleted_account_path, 'a').close() # then deletes the account and verifies it worked self.assertEqual(len(self.account_utils.get_account_list()), 1) self.account_utils.delete_account(account) self.assertEqual(len(self.account_utils.get_account_list()), 0)
class MonitorRollsService(): def __init__(self): keystore_dir = self.get_keystore_path() self.account_utils = AccountUtils(keystore_dir=keystore_dir) self._pyetheroll = None # per address cached merged logs, used to compare with next pulls self.merged_logs = {} self.last_roll_activity = None def run(self): """ Blocking pull loop call. Service will stop after a period of time with no roll activity. """ self.last_roll_activity = time() elapsed = (time() - self.last_roll_activity) while elapsed < NO_ROLL_ACTIVITY_PERDIOD_SECONDS: self.pull_accounts_rolls() sleep(PULL_FREQUENCY_SECONDS) elapsed = (time() - self.last_roll_activity) # service decided to die naturally after no roll activity self.set_auto_restart_service(False) @staticmethod def set_auto_restart_service(restart=True): """ Makes sure the service restarts automatically on Android when killed. """ if platform != 'android': return from jnius import autoclass PythonService = autoclass('org.kivy.android.PythonService') PythonService.mService.setAutoRestartService(restart) @property def pyetheroll(self): """ Gets or creates the Etheroll object. Also recreates the object if the chain_id changed. """ chain_id = self.get_stored_network() if self._pyetheroll is None or self._pyetheroll.chain_id != chain_id: self._pyetheroll = Etheroll(chain_id) return self._pyetheroll @staticmethod def user_data_dir(): """ Fakes kivy.app.App().user_data_dir behavior. On Android, `/sdcard/<app_name>` is returned. """ # TODO: hardcoded app_name = 'etheroll' data_dir = os.path.join('/sdcard', app_name) data_dir = os.path.expanduser(data_dir) if not os.path.exists(data_dir): os.mkdir(data_dir) return data_dir # TODO: merge at least "store.json" const with src/etheroll/store.py @classmethod def get_store_path(cls): """ Returns the full user store path. """ user_data_dir = cls.user_data_dir() store_path = os.path.join(user_data_dir, 'store.json') return store_path @classmethod def get_store(cls): """ Returns user Store object. """ store_path = cls.get_store_path() store = JsonStore(store_path) return store # TODO: refactore and share the one from settings or somewhere @classmethod def get_stored_network(cls): """ Retrieves last stored network value, defaults to Mainnet. """ store = cls.get_store() try: network_dict = store['network'] except KeyError: network_dict = {} network_name = network_dict.get( 'value', ChainID.MAINNET.name) network = ChainID[network_name] return network @classmethod def get_keystore_path(cls): KEYSTORE_DIR_PREFIX = cls.user_data_dir() keystore_dir = os.path.join( KEYSTORE_DIR_PREFIX, KEYSTORE_DIR_SUFFIX) return keystore_dir def pull_account_rolls(self, account): """ Retrieves the merged logs for the given account and compares it with existing cached value. If it differs notifies and updates cache. """ address = "0x" + account.address.hex() merged_logs = self.pyetheroll.get_merged_logs(address=address) try: merged_logs_cached = self.merged_logs[address] except KeyError: # not yet cahed, let's cache it for the first time self.merged_logs[address] = merged_logs return if merged_logs_cached != merged_logs: # since it differs, updates the cache and notifies self.merged_logs[address] = merged_logs self.do_notify(merged_logs) self.last_roll_activity = time() def pull_accounts_rolls(self): accounts = self.account_utils.get_account_list() for account in accounts: self.pull_account_rolls(account) def do_notify(self, merged_logs): """ Notifies the with last roll. If the roll has no bet result, notifies it was just placed on the blockchain, but not yet resolved by the oracle. If it has a result, notifies it. """ merged_log = merged_logs[-1] bet_log = merged_log['bet_log'] bet_result = merged_log['bet_result'] bet_value_ether = bet_log['bet_value_ether'] roll_under = bet_log['roll_under'] ticker = "Ticker" # the bet was just placed, but not resolved by the oracle if bet_result is None: title = "Bet confirmed on chain" message = ( '{bet_value_ether:.{round_digits}f} ETH ' 'to roll under {roll_under}').format(**{ 'bet_value_ether': bet_value_ether, 'round_digits': ROUND_DIGITS, 'roll_under': roll_under}) else: dice_result = bet_result['dice_result'] player_won = dice_result < roll_under sign = '<' if player_won else '>' title = 'You ' title += 'won' if player_won else 'lost' message = '{0} {1} {2}'.format( dice_result, sign, roll_under) kwargs = {'title': title, 'message': message, 'ticker': ticker} notification.notify(**kwargs)
def setUp(self): self.keystore_dir = mkdtemp() self.account_utils = AccountUtils(self.keystore_dir)
class TestAccountUtils(unittest.TestCase): def setUp(self): self.keystore_dir = mkdtemp() self.account_utils = AccountUtils(self.keystore_dir) def tearDown(self): shutil.rmtree(self.keystore_dir, ignore_errors=True) def test_new_account(self): """ Simple account creation test case. 1) verifies the current account list is empty 2) creates a new account and verify we can retrieve it 3) tries to unlock the account """ # 1) verifies the current account list is empty account_list = self.account_utils.get_account_list() self.assertEqual(len(account_list), 0) # 2) creates a new account and verify we can retrieve it password = PASSWORD account = self.account_utils.new_account(password) account_list = self.account_utils.get_account_list() self.assertEqual(len(account_list), 1) self.assertEqual(account, self.account_utils.get_account_list()[0]) # 3) tries to unlock the account # it's unlocked by default after creation self.assertFalse(account.locked) # let's lock it and unlock it back account.lock() self.assertTrue(account.locked) account.unlock(password) self.assertFalse(account.locked) def test_deleted_account_dir(self): """ The deleted_account_dir() helper method should be working with and without trailing slash. """ expected_deleted_keystore_dir = '/tmp/keystore-deleted' keystore_dirs = [ # without trailing slash '/tmp/keystore', # with one trailing slash '/tmp/keystore/', # with two trailing slashes '/tmp/keystore//', ] for keystore_dir in keystore_dirs: self.assertEqual( AccountUtils.deleted_account_dir(keystore_dir), expected_deleted_keystore_dir) def test_delete_account(self): """ Creates a new account and delete it. Then verify we can load the account from the backup/trash location. """ password = PASSWORD account = self.account_utils.new_account(password) address = account.address self.assertEqual(len(self.account_utils.get_account_list()), 1) # deletes the account and verifies it's not loaded anymore self.account_utils.delete_account(account) self.assertEqual(len(self.account_utils.get_account_list()), 0) # even recreating the AccountUtils object self.account_utils = AccountUtils(self.keystore_dir) self.assertEqual(len(self.account_utils.get_account_list()), 0) # tries to reload it from the backup/trash location deleted_keystore_dir = AccountUtils.deleted_account_dir( self.keystore_dir) self.account_utils = AccountUtils(deleted_keystore_dir) self.assertEqual(len(self.account_utils.get_account_list()), 1) self.assertEqual( self.account_utils.get_account_list()[0].address, address) def test_delete_account_already_exists(self): """ If the destination (backup/trash) directory where the account is moved already exists, it should be handled gracefully. This could happens if the account gets deleted, then reimported and deleted again, refs: https://github.com/AndreMiras/PyWallet/issues/88 """ password = PASSWORD account = self.account_utils.new_account(password) # creates a file in the backup/trash folder that would conflict # with the deleted account deleted_keystore_dir = AccountUtils.deleted_account_dir( self.keystore_dir) os.makedirs(deleted_keystore_dir) account_filename = os.path.basename(account.path) deleted_account_path = os.path.join( deleted_keystore_dir, account_filename) # create that file open(deleted_account_path, 'a').close() # then deletes the account and verifies it worked self.assertEqual(len(self.account_utils.get_account_list()), 1) self.account_utils.delete_account(account) self.assertEqual(len(self.account_utils.get_account_list()), 0)
class PyWalib: def __init__(self, keystore_dir=None, chain_id=ChainID.MAINNET): if keystore_dir is None: keystore_dir = PyWalib.get_default_keystore_path() self.keystore_dir = keystore_dir self.account_utils = AccountUtils(keystore_dir=self.keystore_dir) self.chain_id = chain_id self.provider = HTTPProviderFactory.create(self.chain_id) self.web3 = Web3(self.provider) @staticmethod def handle_etherscan_error(response_json): """ Raises an exception on unexpected response. """ status = response_json["status"] message = response_json["message"] if status != "1": if message == "No transactions found": raise NoTransactionFoundException() else: raise UnknownEtherscanException(response_json) assert message == "OK" @staticmethod def get_balance(address, chain_id=ChainID.MAINNET): """ Retrieves the balance from etherscan.io. The balance is returned in ETH rounded to the second decimal. """ address = to_checksum_address(address) url = get_etherscan_prefix(chain_id) url += '?module=account&action=balance' url += '&address=%s' % address url += '&tag=latest' if ETHERSCAN_API_KEY: '&apikey=%' % ETHERSCAN_API_KEY # TODO: handle 504 timeout, 403 and other errors from etherscan response = requests.get(url) response_json = response.json() PyWalib.handle_etherscan_error(response_json) balance_wei = int(response_json["result"]) balance_eth = balance_wei / float(pow(10, 18)) balance_eth = round(balance_eth, ROUND_DIGITS) return balance_eth def get_balance_web3(self, address): """ The balance is returned in ETH rounded to the second decimal. """ address = to_checksum_address(address) balance_wei = self.web3.eth.getBalance(address) balance_eth = balance_wei / float(pow(10, 18)) balance_eth = round(balance_eth, ROUND_DIGITS) return balance_eth @staticmethod def get_transaction_history(address, chain_id=ChainID.MAINNET): """ Retrieves the transaction history from etherscan.io. """ address = to_checksum_address(address) url = get_etherscan_prefix(chain_id) url += '?module=account&action=txlist' url += '&sort=asc' url += '&address=%s' % address if ETHERSCAN_API_KEY: '&apikey=%' % ETHERSCAN_API_KEY # TODO: handle 504 timeout, 403 and other errors from etherscan response = requests.get(url) response_json = response.json() PyWalib.handle_etherscan_error(response_json) transactions = response_json['result'] for transaction in transactions: value_wei = int(transaction['value']) value_eth = value_wei / float(pow(10, 18)) value_eth = round(value_eth, ROUND_DIGITS) from_address = to_checksum_address(transaction['from']) to_address = transaction['to'] # on contract creation, "to" is replaced by the "contractAddress" if not to_address: to_address = transaction['contractAddress'] to_address = to_checksum_address(to_address) sent = from_address == address received = not sent extra_dict = { 'value_eth': value_eth, 'sent': sent, 'received': received, 'from_address': from_address, 'to_address': to_address, } transaction.update({'extra_dict': extra_dict}) # sort by timeStamp transactions.sort(key=lambda x: x['timeStamp']) return transactions @staticmethod def get_out_transaction_history(address, chain_id=ChainID.MAINNET): """ Retrieves the outbound transaction history from Etherscan. """ transactions = PyWalib.get_transaction_history(address, chain_id) out_transactions = [] for transaction in transactions: if transaction['extra_dict']['sent']: out_transactions.append(transaction) return out_transactions # TODO: can be removed since the migration to web3 @staticmethod def get_nonce(address, chain_id=ChainID.MAINNET): """ Gets the nonce by counting the list of outbound transactions from Etherscan. """ try: out_transactions = PyWalib.get_out_transaction_history( address, chain_id) except NoTransactionFoundException: out_transactions = [] nonce = len(out_transactions) return nonce @staticmethod def handle_web3_exception(exception: ValueError): """ Raises the appropriated typed exception on web3 ValueError exception. """ error = exception.args[0] code = error.get("code") if code in [-32000, -32010]: raise InsufficientFundsException(error) else: raise UnknownEtherscanException(error) def transact(self, to, value=0, data='', sender=None, gas=25000, gasprice=DEFAULT_GAS_PRICE_GWEI * (10**9)): """ Signs and broadcasts a transaction. Returns transaction hash. """ address = sender or self.get_main_account().address from_address_normalized = to_checksum_address(address) nonce = self.web3.eth.getTransactionCount(from_address_normalized) transaction = { 'chainId': self.chain_id.value, 'gas': gas, 'gasPrice': gasprice, 'nonce': nonce, 'value': value, } account = self.account_utils.get_by_address(address) private_key = account.privkey signed_tx = self.web3.eth.account.signTransaction( transaction, private_key) try: tx_hash = self.web3.eth.sendRawTransaction( signed_tx.rawTransaction) except ValueError as e: self.handle_web3_exception(e) return tx_hash @staticmethod def deleted_account_dir(keystore_dir): """ Given a `keystore_dir`, returns the corresponding `deleted_keystore_dir`. >>> keystore_dir = '/tmp/keystore' >>> PyWalib.deleted_account_dir(keystore_dir) u'/tmp/keystore-deleted' >>> keystore_dir = '/tmp/keystore/' >>> PyWalib.deleted_account_dir(keystore_dir) u'/tmp/keystore-deleted' """ keystore_dir = keystore_dir.rstrip('/') keystore_dir_name = os.path.basename(keystore_dir) deleted_keystore_dir_name = "%s-deleted" % (keystore_dir_name) deleted_keystore_dir = os.path.join(os.path.dirname(keystore_dir), deleted_keystore_dir_name) return deleted_keystore_dir # TODO: update docstring # TODO: update security_ratio def new_account(self, password, security_ratio=None): """ Creates an account on the disk and returns it. security_ratio is a ratio of the default PBKDF2 iterations. Ranging from 1 to 100 means 100% of the iterations. """ account = self.account_utils.new_account(password=password) return account def delete_account(self, account): """ Deletes the given `account` from the `keystore_dir` directory. In fact, moves it to another location; another directory at the same level. """ self.account_utils.delete_account(account) def update_account_password(self, account, new_password, current_password=None): """ The current_password is optional if the account is already unlocked. """ self.account_utils.update_account_password(account, new_password, current_password) @staticmethod def get_default_keystore_path(): """ Returns the keystore path, which is the same as the default pyethapp one. """ keystore_dir = os.path.join(KEYSTORE_DIR_PREFIX, KEYSTORE_DIR_SUFFIX) return keystore_dir def get_account_list(self): """ Returns the Account list. """ accounts = self.account_utils.get_account_list() return accounts def get_main_account(self): """ Returns the main Account. """ account = self.get_account_list()[0] return account