From 0b912b99b6366fe2dd14166c0dcdf951983b0e39 Mon Sep 17 00:00:00 2001 From: nolash Date: Sat, 30 Oct 2021 13:19:31 +0200 Subject: [PATCH] Add role listing to cic-eth tag cli tool --- apps/cic-eth/cic_eth/api/admin.py | 32 +++++++++++-- apps/cic-eth/cic_eth/db/models/role.py | 18 ++++++- apps/cic-eth/cic_eth/eth/account.py | 35 ++++++++++++-- apps/cic-eth/cic_eth/runnable/tag.py | 25 ++++++++-- apps/cic-eth/tests/task/api/test_admin.py | 6 +-- apps/cic-eth/tests/task/test_task_account.py | 50 +++++++++++++++++++- apps/contract-migration/4_init_custodial.sh | 4 +- 7 files changed, 150 insertions(+), 20 deletions(-) diff --git a/apps/cic-eth/cic_eth/api/admin.py b/apps/cic-eth/cic_eth/api/admin.py index 33bc2079..0edaa9e7 100644 --- a/apps/cic-eth/cic_eth/api/admin.py +++ b/apps/cic-eth/cic_eth/api/admin.py @@ -123,7 +123,7 @@ class AdminApi: return s_lock.apply_async() - def tag_account(self, tag, address_hex, chain_spec): + def tag_account(self, chain_spec, tag, address): """Persistently associate an address with a plaintext tag. Some tags are known by the system and is used to resolve addresses to use for certain transactions. @@ -138,7 +138,7 @@ class AdminApi: 'cic_eth.eth.account.set_role', [ tag, - address_hex, + address, chain_spec.asdict(), ], queue=self.queue, @@ -146,6 +146,30 @@ class AdminApi: return s_tag.apply_async() + def get_tag_account(self, chain_spec, tag=None, address=None): + if address != None: + s_tag = celery.signature( + 'cic_eth.eth.account.role', + [ + address, + chain_spec.asdict(), + ], + queue=self.queue, + ) + + else: + s_tag = celery.signature( + 'cic_eth.eth.account.role_account', + [ + tag, + chain_spec.asdict(), + ], + queue=self.queue, + ) + + return s_tag.apply_async() + + def have_account(self, address_hex, chain_spec): s_have = celery.signature( 'cic_eth.eth.account.have', @@ -503,7 +527,7 @@ class AdminApi: queue=self.queue, ) t = s.apply_async() - role = t.get() + role = t.get()[0][1] if role != None: tx['sender_description'] = role @@ -556,7 +580,7 @@ class AdminApi: queue=self.queue, ) t = s.apply_async() - role = t.get() + role = t.get()[0][1] if role != None: tx['recipient_description'] = role diff --git a/apps/cic-eth/cic_eth/db/models/role.py b/apps/cic-eth/cic_eth/db/models/role.py index 9343f8d4..a9a12a65 100644 --- a/apps/cic-eth/cic_eth/db/models/role.py +++ b/apps/cic-eth/cic_eth/db/models/role.py @@ -24,8 +24,22 @@ class AccountRole(SessionBase): tag = Column(Text) address_hex = Column(String(42)) - - # TODO: + + @staticmethod + def all(session=None): + session = SessionBase.bind_session(session) + + pairs = [] + + q = session.query(AccountRole.tag, AccountRole.address_hex) + for r in q.all(): + pairs.append((r[1], r[0]),) + + SessionBase.release_session(session) + + return pairs + + @staticmethod def get_address(tag, session): """Get Ethereum address matching the given tag diff --git a/apps/cic-eth/cic_eth/eth/account.py b/apps/cic-eth/cic_eth/eth/account.py index 61ddfba5..5b83421f 100644 --- a/apps/cic-eth/cic_eth/eth/account.py +++ b/apps/cic-eth/cic_eth/eth/account.py @@ -266,19 +266,46 @@ def set_role(self, tag, address, chain_spec_dict): @celery_app.task(bind=True, base=BaseTask) def role(self, address, chain_spec_dict): - """Return account role for address + """Return account role for address and/or role :param account: Account to check :type account: str, 0x-hex - :param chain_str: Chain spec string representation - :type chain_str: str + :param chain_spec_dict: Chain spec dict representation + :type chain_spec_dict: dict :returns: Account, or None if not exists :rtype: Varies """ session = self.create_session() role_tag = AccountRole.role_for(address, session=session) session.close() - return role_tag + return [(address, role_tag,)] + + +@celery_app.task(bind=True, base=BaseTask) +def role_account(self, role_tag, chain_spec_dict): + """Return address for role. + + If the role parameter is None, will return addresses for all roles. + + :param role_tag: Role to match + :type role_tag: str + :param chain_spec_dict: Chain spec dict representation + :type chain_spec_dict: dict + :returns: List with a single account/tag pair for a single tag, or a list of account and tag pairs for all tags + :rtype: list + """ + session = self.create_session() + + pairs = None + if role_tag != None: + addr = AccountRole.get_address(role_tag, session=session) + pairs = [(addr, role_tag,)] + else: + pairs = AccountRole.all(session=session) + + session.close() + + return pairs @celery_app.task(bind=True, base=CriticalSQLAlchemyTask) diff --git a/apps/cic-eth/cic_eth/runnable/tag.py b/apps/cic-eth/cic_eth/runnable/tag.py index b4bdd83a..7231c7ab 100644 --- a/apps/cic-eth/cic_eth/runnable/tag.py +++ b/apps/cic-eth/cic_eth/runnable/tag.py @@ -8,6 +8,7 @@ import re # external imports import cic_eth.cli from chainlib.chain import ChainSpec +from chainlib.eth.address import is_address from xdg.BaseDirectory import xdg_config_home # local imports @@ -21,12 +22,18 @@ logg = logging.getLogger() arg_flags = cic_eth.cli.argflag_std_base | cic_eth.cli.Flag.UNSAFE | cic_eth.cli.Flag.CHAIN_SPEC local_arg_flags = cic_eth.cli.argflag_local_taskcallback argparser = cic_eth.cli.ArgumentParser(arg_flags) -argparser.add_positional('tag', type=str, help='address tag') -argparser.add_positional('address', type=str, help='address') +argparser.add_argument('--set', action='store_true', help='sets the given tag') +argparser.add_argument('--tag', type=str, help='operate on the given tag') +argparser.add_positional('address', required=False, type=str, help='address associated with tag') argparser.process_local_flags(local_arg_flags) args = argparser.parse_args() -config = cic_eth.cli.Config.from_args(args, arg_flags, local_arg_flags) +extra_args = { + 'set': None, + 'tag': None, + 'address': None, + } +config = cic_eth.cli.Config.from_args(args, arg_flags, local_arg_flags, extra_args=extra_args) celery_app = cic_eth.cli.CeleryApp.from_config(config) @@ -39,7 +46,17 @@ api = AdminApi(None) def main(): - admin_api.tag_account(args.tag, args.address, chain_spec) + if config.get('_ADDRESS') != None and not is_address(config.get('_ADDRESS')): + sys.stderr.write('Invalid address {}'.format(config.get('_ADDRESS'))) + sys.exit(1) + + if config.get('_SET'): + admin_api.tag_account(chain_spec, config.get('_TAG'), config.get('_ADDRESS')) + else: + t = admin_api.get_tag_account(chain_spec, tag=config.get('_TAG'), address=config.get('_ADDRESS')) + r = t.get() + for v in r: + sys.stdout.write('{}\t{}\n'.format(v[1], v[0])) if __name__ == '__main__': diff --git a/apps/cic-eth/tests/task/api/test_admin.py b/apps/cic-eth/tests/task/api/test_admin.py index 363fb8d7..d732fb96 100644 --- a/apps/cic-eth/tests/task/api/test_admin.py +++ b/apps/cic-eth/tests/task/api/test_admin.py @@ -103,11 +103,11 @@ def test_tag_account( api = AdminApi(eth_rpc, queue=None) - t = api.tag_account('foo', agent_roles['ALICE'], default_chain_spec) + t = api.tag_account(default_chain_spec, 'foo', agent_roles['ALICE']) t.get() - t = api.tag_account('bar', agent_roles['BOB'], default_chain_spec) + t = api.tag_account(default_chain_spec, 'bar', agent_roles['BOB']) t.get() - t = api.tag_account('bar', agent_roles['CAROL'], default_chain_spec) + t = api.tag_account(default_chain_spec, 'bar', agent_roles['CAROL']) t.get() assert AccountRole.get_address('foo', init_database) == tx_normalize.wallet_address(agent_roles['ALICE']) diff --git a/apps/cic-eth/tests/task/test_task_account.py b/apps/cic-eth/tests/task/test_task_account.py index d1c1c3d2..58f1b499 100644 --- a/apps/cic-eth/tests/task/test_task_account.py +++ b/apps/cic-eth/tests/task/test_task_account.py @@ -141,9 +141,57 @@ def test_role_task( ) t = s.apply_async() r = t.get() - assert r == 'foo' + assert r[0][0] == address + assert r[0][1] == 'foo' +def test_get_role_task( + init_database, + celery_session_worker, + default_chain_spec, + ): + address_foo = '0x' + os.urandom(20).hex() + role_foo = AccountRole.set('foo', address_foo) + init_database.add(role_foo) + + address_bar = '0x' + os.urandom(20).hex() + role_bar = AccountRole.set('bar', address_bar) + init_database.add(role_bar) + + init_database.commit() + + s = celery.signature( + 'cic_eth.eth.account.role_account', + [ + 'bar', + default_chain_spec.asdict(), + ], + queue=None, + ) + t = s.apply_async() + r = t.get() + assert r[0][0] == address_bar + assert r[0][1] == 'bar' + + s = celery.signature( + 'cic_eth.eth.account.role_account', + [ + None, + default_chain_spec.asdict(), + ], + queue=None, + ) + t = s.apply_async() + r = t.get() + x_tags = ['foo', 'bar'] + x_addrs = [address_foo, address_bar] + + for v in r: + x_addrs.remove(v[0]) + x_tags.remove(v[1]) + + assert len(x_tags) == 0 + assert len(x_addrs) == 0 def test_gift( init_database, diff --git a/apps/contract-migration/4_init_custodial.sh b/apps/contract-migration/4_init_custodial.sh index 27b08d61..6997e910 100644 --- a/apps/contract-migration/4_init_custodial.sh +++ b/apps/contract-migration/4_init_custodial.sh @@ -29,11 +29,11 @@ REDIS_HOST_CALLBACK=${REDIS_HOST_CALLBACK:-$REDIS_HOST} REDIS_PORT_CALLBACK=${REDIS_PORT_CALLBACK:-$REDIS_PORT} >&2 echo -e "\033[;96mcreate account for gas gifter\033[;39m" gas_gifter=`cic-eth-create --redis-timeout 120 $DEV_DEBUG_FLAG --redis-host-callback $REDIS_HOST_CALLBACK --redis-port-callback $REDIS_PORT_CALLBACK --no-register` -cic-eth-tag -i $CHAIN_SPEC GAS_GIFTER $gas_gifter +cic-eth-tag -i $CHAIN_SPEC --set --tag GAS_GIFTER $gas_gifter >&2 echo -e "\033[;96mcreate account for accounts index writer\033[;39m" accounts_index_writer=`cic-eth-create --redis-timeout 120 $DEV_DEBUG_FLAG --redis-host-callback $REDIS_HOST_CALLBACK --redis-port-callback $REDIS_PORT_CALLBACK --no-register` -cic-eth-tag -i $CHAIN_SPEC ACCOUNT_REGISTRY_WRITER $accounts_index_writer +cic-eth-tag -i $CHAIN_SPEC --set --tag ACCOUNT_REGISTRY_WRITER $accounts_index_writer # Assign system writer for accounts index