def systest_smart_contract_uapp(): log.info('Running systest smart contract UApp') d_conf = json.loads(Path('data/demo_config.json').read_text()) appnames = ['app1', 'app2', 'app3'] d_apps = d_conf['demo_apps'] conf = UnificationConfig() eos_client = Client( nodes=[f"http://{conf['eos_rpc_ip']}:{conf['eos_rpc_port']}"]) for appname in appnames: log.info("------------------------------------------") app_data = d_apps[appname] conf_db_schemas = app_data['db_schemas'] uapp_sc = UnificationUapp(eos_client, app_data['eos_sc_account']) log.info("Check DB Schemas are correctly configured") for schema_obj in conf_db_schemas: log.info(f"Check schema {schema_obj['schema_name']}") conf_schema = schema_obj['schema'] log.info(f"Expecting - config.json: {conf_schema}") # version set to 1, since that's the hard coded version used in # accounts.validate_with_mother uapp_contract_schema = uapp_sc.get_db_schema_by_pkey(0) log.info(f"Actual - UApp Smart Contract: " f"{uapp_contract_schema['schema']}") assert (conf_schema == uapp_contract_schema['schema']) is True
def __init__(self, eos_client, uapp_contract_acc, requesting_app, users): """ :param users: A list of users we want to obtain data for. If an empty list is provided, then data for all permissible users are provided. """ self.__uapp_contract_acc = uapp_contract_acc self.__requesting_app = requesting_app self.__haiku_conf = UnificationConfig() self.__my_mother = UnificationMother(eos_client, uapp_contract_acc, get_cleos(), get_ipfs_client()) self.__my_uapp_sc = UnificationUapp(eos_client, uapp_contract_acc) self.__my_lookup = UnificationLookup(default_lookup_db()) self.__users = None if len(users) == 0 else users self.__my_db_schemas = self.__my_uapp_sc.get_all_db_schemas() self.__db_schema_maps = {} self.__granted = [] self.__granted_field_lookup = {} self.__revoked = [] self.__raw_data = None self.__native_user_meta = self.__my_lookup.get_native_user_meta() for pkey, db_schema in self.__my_db_schemas.items(): schema_map = self.__my_lookup.get_schema_map(pkey) self.__db_schema_maps[pkey] = schema_map self.__generate_data()
def get_public_key(app_name): eos_client = get_eos_rpc_client() uapp_sc = UnificationUapp(eos_client, app_name) public_key_hash = uapp_sc.get_public_key_hash(app_name) store = get_ipfs_client() public_key = store.cat_file(public_key_hash) return serialization.load_pem_public_key(public_key, backend=default_backend())
def run_test_uapp(self, app): print("Loading UApp Contract for: ", app) eos_client = Client(nodes=[self.cleos.get_nodeos_url()]) u_uapp = UnificationUapp(eos_client, app) print("Data Schemas:") print(u_uapp.get_all_db_schemas()) print("-----------------------------------")
def obtain_data(keystore, eos_account_name, eos_client, uapp_contract_acc, users, request_id=None): """ :param eos_account_name: The account name of the requesting App (Data Consumer). :param eos_client: EOS RPC Client :param uapp_contract_acc: The account name of the providing App (Data Provider). :param users: The users to obtain data for. None to get all available users :param request_id: Primary Key for the data request held in the Consumer's UApp smart contract """ data_factory = UnificationDataFactory(eos_client, uapp_contract_acc, eos_account_name, users) body = {'data': data_factory.get_raw_data()} d = bundle(keystore, uapp_contract_acc, eos_account_name, body, 'Success') # load UApp SC for requesting app uapp_sc = UnificationUapp(eos_client, eos_account_name) # generate checksum data_hash = hashlib.sha224(str(d['payload']).encode('utf-8')).hexdigest() # temporarily allow uapp_contract_acc@modreq to # interact with consumer's contract eosio_cleos = EosioCleos(False) eosio_cleos.run([ "set", "action", "permission", uapp_contract_acc, eos_account_name, 'updatereq', 'modreq', '-p', f'{uapp_contract_acc}@active' ]) # write to Consumer's smart contract transaction_id = uapp_sc.update_data_request(request_id, uapp_contract_acc, data_hash, "test") # Remove permission association for action in consumer's contract eosio_cleos.run([ "set", "action", "permission", uapp_contract_acc, eos_account_name, 'updatereq', 'NULL', '-p', f'{uapp_contract_acc}@active' ]) # check transaction has been processed if transaction_id is not None: return flask.jsonify(d), 200 else: return bc_transaction_error()
def uapp_store(): """ Display a list of valid apps, and their schemas. Allow option to initiate and process a data transfer \b """ requesting_app = os.environ['app_name'] click.echo(bold("UApp Store")) eos_client = get_eos_rpc_client() valid_apps = eos_client.get_table_rows("unif.mother", "unif.mother", "validapps", True, 0, -1, -1) uapp_store_dict = {} store_key = 1 for va in valid_apps['rows']: data_provider = eosio_account.name_to_string( int(va['uapp_contract_acc'])) if int(va['is_valid']) == 1 and data_provider != requesting_app: uapp_sc = UnificationUapp(eos_client, data_provider) db_schemas = uapp_sc.get_all_db_schemas() click.echo(bold(f"Data Provider: {data_provider}")) for schema_pkey, db_schema in db_schemas.items(): schema = db_schema['schema'] click.echo(bold(f"Option {store_key}:")) click.echo(" Data available:") for field in schema['fields']: click.echo(f" {field['name']}, {field['type']}") click.echo( f" Scheduled Price: {db_schema['price_sched']} UND") click.echo(f" Ad-hoc Price: {db_schema['price_adhoc']} UND") click.echo(" Availability: daily") d = { 'provider': data_provider, 'schema_pkey': schema_pkey, 'price': db_schema['price_sched'] } uapp_store_dict[store_key] = d store_key += 1 request_id = int( input(f"Select option 1 - {(store_key - 1)} to generate " f"a data request, or '0' to exit:")) if 0 < request_id <= store_key: data_request = uapp_store_dict[request_id] __request_from_uapp_store(data_request) else: click.echo("Exit Uapp Store")
def get_proof(): conf = app.unification_config d = flask.request.get_json() user = d['user'] consumer = d['consumer'] ipfs_hash = d['ipfs_hash'] schema_id = d['schema_id'] provider_uapp = UnificationUapp(get_eos_rpc_client(), conf['uapp_contract']) permission_db = PermissionBatchDatabase(pb_default_db()) permissions = UnifPermissions(get_ipfs_client(), provider_uapp, permission_db) if ipfs_hash is not None: permissions.load_perms_from_ipfs(ipfs_hash) else: permissions.load_consumer_perms(consumer) proof = permissions.get_proof(user, schema_id=schema_id) # ToDo: send as JWT return_d = {'proof': proof} return flask.jsonify(return_d), 200
def schemas(app_name): """ Obtain data source information about an App. \b :param app_name: The EOS app account name to query. """ eos_client = get_eos_rpc_client() uapp_sc = UnificationUapp(eos_client, app_name) click.echo(f"{app_name} has the following Schemas:\n") for key, schema in uapp_sc.get_all_db_schemas().items(): click.echo(f"Schema ID {schema['pkey']}:") click.echo(schema['schema'])
def systest_merkle_proof_permissions(): ipfs = get_ipfs_client() users, consumers, providers = compile_actors() for provider in providers: log.debug(f'run systest_merkle_proof_' f'permissions for Provider {provider}') provider_uapp = UnificationUapp(get_eos_rpc_client(), provider) permission_db = PermissionBatchDatabase(pb_default_db()) permissions = UnifPermissions(ipfs, provider_uapp, permission_db) for consumer in consumers: if consumer != provider: log.debug(f'Provider {provider}: load ' f'permissions for Consumer {consumer}') permissions.load_consumer_perms(consumer) permissions_obj = permissions.get_all_perms() tree = MerkleTree() for user, perm in permissions_obj['permissions'].items(): for schema_id, schema_perm in perm.items(): tree.add_leaf(json.dumps(schema_perm)) tree.grow_tree() log.debug(f"Generated merkle root: {tree.get_root_str()}") log.debug(f"Recorded merkle root: " f"{permissions_obj['merkle_root']}") for user, perm in permissions_obj['permissions'].items(): for schema_id, schema_perm in perm.items(): requested_leaf = json.dumps(schema_perm) proof_chain = tree.get_proof(requested_leaf, is_hashed=False) log.debug(f'Permission leaf for {user}:' f' {requested_leaf}') log.debug(f'Proof chain for {user} - ' f'Schema {schema_id} ' f'permission leaf: ' f'{json.dumps(proof_chain)}') # simulate only having access to leaf, # root and proof chain for leaf verify_tree = MerkleTree() is_good = verify_tree.verify_leaf( requested_leaf, permissions_obj['merkle_root'], proof_chain, is_hashed=False) log.debug(f'Leaf is valid: {is_good}') assert is_good
def permissions(user): """ Display user permissions. \b :param user: The EOS user account name to query. """ eos_rpc_client = get_eos_rpc_client() ipfs_client = get_ipfs_client() apps = [] valid_apps = eos_rpc_client.get_table_rows( "unif.mother", "unif.mother", "validapps", True, 0, -1, -1) for va in valid_apps['rows']: apps.append(eosio_account.name_to_string(int(va['uapp_contract_acc']))) click.echo(f"{bold(user)} Permissions overview:") for provider in apps: click.echo(f'Provider: {bold(provider)}') for consumer in apps: if consumer != provider: click.echo(f' Consumer: {bold(consumer)}') uapp_sc = UnificationUapp(eos_rpc_client, provider) ipfs_hash, merkle_root = uapp_sc.get_ipfs_perms_for_req_app( consumer) if ipfs_hash is not None and ipfs_hash != ZERO_MASK: permissions_str = ipfs_client.get_json(ipfs_hash) permissions_json = json.loads(permissions_str) user_perms = permissions_json[user] for schema_id, perms in user_perms.items(): click.echo(f' Schema ID: {schema_id}') if perms['perms'] == '': click.echo(' Granted: False') else: click.echo(' Granted: True') click.echo(f" Fields: {perms['perms']}") else: click.echo('Nothing set')
def __request_from_uapp_store(data_request): """ Receives a data request from the UApp Store, and processes the request \b :param data_request: Dict containing request parameters """ requesting_app = os.environ['app_name'] password = os.environ['keystore'] click.echo("Processing request from UApp Store:") click.echo(data_request) eos_client = get_eos_rpc_client() # Write the data request to the Consumer's smart contract uapp_sc = UnificationUapp(eos_client, requesting_app) latest_req_id = uapp_sc.init_data_request(data_request['provider'], data_request['schema_pkey'], "0", data_request['price']) request_hash = f"{data_request['provider']}-{data_request['schema_pkey']}" \ f"-{latest_req_id}.dat" provider_name = data_request['provider'] mother = UnificationMother(eos_client, provider_name, get_cleos(), get_ipfs_client()) provider_obj = Provider(provider_name, 'https', mother) req_hash = f'request-{request_hash}' click.echo(f'App {requesting_app} is requesting data from ' f'{provider_obj.name}') encoded_password = str.encode(password) keystore = UnificationKeystore(encoded_password) client = HaikuDataClient(keystore) data_path = client.make_data_request(requesting_app, provider_obj, None, req_hash, latest_req_id) click.echo(f'Data written to: {data_path}') click.echo(f'View using: haiku view {provider_obj.name} {request_hash}')
def systest_ingest(requesting_app, providing_app, user, balances): log.info(f'Testing Fetch ingestion: {requesting_app} ' f'is requesting data from {providing_app}') request_hash = f'data-request-{providing_app}-{requesting_app}' app_config = demo_config['demo_apps'][providing_app] port = app_config['rpc_server_port'] eos_client = get_eos_rpc_client() mother = UnificationMother(eos_client, providing_app, get_cleos(), get_ipfs_client()) provider_obj = Provider(providing_app, 'https', mother) password = demo_config['system'][requesting_app]['password'] encoded_password = str.encode(password) keystore = UnificationKeystore(encoded_password, app_name=requesting_app, keystore_path=Path('data/keys')) conf = UnificationConfig() eos_client = Client( nodes=[f"http://{conf['eos_rpc_ip']}:{conf['eos_rpc_port']}"]) consumer_uapp_sc = UnificationUapp(eos_client, requesting_app) price_sched = demo_config['demo_apps'][providing_app]['db_schemas'][0][ 'price_sched'] latest_req_id = consumer_uapp_sc.init_data_request(provider_obj.name, "0", "0", price_sched) client = HaikuDataClient(keystore) client.make_data_request(requesting_app, provider_obj, user, request_hash, latest_req_id) client.read_data_from_store(provider_obj, request_hash) # Update the system test record of the balances balances[requesting_app] = balances[requesting_app] - price_sched und_rewards = UndRewards(providing_app, price_sched) balances[providing_app] = (balances[providing_app] + und_rewards.calculate_reward(is_user=False)) return balances
def systest_check_permission_requests(): ipfs = get_ipfs_client() users, consumers, providers = compile_actors() for provider in providers: log.debug(f'run systest_check_permission_requests' f' for Provider {provider}') provider_uapp = UnificationUapp(get_eos_rpc_client(), provider) permission_db = PermissionBatchDatabase(pb_default_db()) permissions = UnifPermissions(ipfs, provider_uapp, permission_db) for consumer in consumers: if consumer != provider: log.debug(f'Provider {provider}: load permissions ' f'for Consumer {consumer}') permissions.load_consumer_perms(consumer) for user in users: user_permissions = permissions.get_user_perms_for_all_schemas( user) for schema_id, user_perms in user_permissions.items(): log.debug(f'User {user}, ' f'Schema {schema_id}: {user_perms}') is_valid = permissions.verify_permission(user_perms) log.debug(f'Perm sig valid: {is_valid}') assert is_valid demo_conf_check = demo_config['demo_permissions'][ user][consumer][provider] demo_conf_fields = demo_conf_check['fields'] demo_conf_granted = demo_conf_check['granted'] demo_conf_schema_id = demo_conf_check['schema_id'] assert int(demo_conf_schema_id) == int(schema_id) if demo_conf_granted: log.debug("Permission granted") log.debug(f"Demo fields: {demo_conf_fields}, " f"recorded fields: " f"{user_perms['perms']}") assert demo_conf_fields == user_perms['perms'] else: log.debug("Permission not granted. Recorded " "perms should be empty") log.debug(f"Recorded fields: " f"{user_perms['perms']}") assert user_perms['perms'] == ''
def data_request(): try: d = flask.request.get_json() # Validate requesting app against smart contracts # config is this Haiku Node's config fle, containing its UApp # Smart Contract account/address and the EOS RPC server/port used for # communicating with the blockchain. conf = app.unification_config sender = d['eos_account_name'] recipient = conf['uapp_contract'] if sender == recipient: return error_request_self() bundle_d = unbundle(app.keystore, sender, d) eos_client = get_eos_rpc_client() # Init the validation class for THIS Haiku, and validate the # REQUESTING APP. v = UnificationAppScValidation(eos_client, d['eos_account_name']) # If the REQUESTING APP is valid according to MOTHER, then we can # generate the data. If not, return an invalid_app response if v.valid(): users = bundle_d.get('users') request_id = bundle_d.get('request_id') # before processing data, check for any stashed permissions ipfs = get_ipfs_client() provider_uapp = UnificationUapp(eos_client, conf['uapp_contract']) permission_db = PermissionBatchDatabase(pb_default_db()) permissions = UnifPermissions(ipfs, provider_uapp, permission_db) permissions.check_and_process_stashed(sender) return obtain_data(app.keystore, sender, eos_client, conf['uapp_contract'], users, request_id) else: return invalid_app() except InvalidSignature: return invalid_response() except Exception as e: logger.exception(e) return generic_error()
def fetch(provider, request_hash, user): """ Fetch data from an App to this App in an Enterprise/DSP/B2B environment, where data request is agreed external to the UApp Store. \b :param provider: The app name of the data provider. :param request_hash: The particular piece of data in concern. :param user: Obtain data for a specific user EOS user account. :return: """ requesting_app = os.environ['app_name'] password = os.environ['keystore'] # Write the data request to the Consumer's UApp smart contract eos_client = get_eos_rpc_client() mother = UnificationMother(eos_client, provider, get_cleos(), get_ipfs_client()) provider = Provider(provider, 'https', mother) req_hash = f'request-{request_hash}' suffix = 'for all users' if user is None else f'for {user}' click.echo(f'App {requesting_app} is requesting data from {provider}' f' {suffix}') encoded_password = str.encode(password) keystore = UnificationKeystore(encoded_password) # tmp - get the price for the transfer from Schema[0] in provider's UApp SC # This will possibly be determined externally as part of the B2B agreement provider_uapp_sc = UnificationUapp(eos_client, provider.name) db_schema = provider_uapp_sc.get_db_schema_by_pkey( 0) # tmp - only 1 schema sched_price = db_schema['price_sched'] # initiate request in Consumer's UApp SC consumer_uapp_sc = UnificationUapp(eos_client, requesting_app) latest_req_id = consumer_uapp_sc.init_data_request(provider.name, "0", "0", sched_price) client = HaikuDataClient(keystore) data_path = client.make_data_request(requesting_app, provider, user, req_hash, latest_req_id) click.echo(f'Data written to: {data_path}') click.echo(f'View using: haiku view {provider.name} {request_hash}')
def make_data_request(self, requesting_app, providing_app: Provider, user, request_hash, request_id): # Check if the providing app is valid according to MOTHER eos_client = get_eos_rpc_client() v = UnificationAppScValidation(eos_client, providing_app.name) if not v.valid(): raise Exception(f"Providing App {providing_app.name} is " f"NOT valid according to MOTHER") body = self.transform_request_id(user, request_hash, request_id) payload = bundle(self.keystore, requesting_app, providing_app.name, body, 'Success') r = providing_app.post('data_request', payload) d = r.json() if r.status_code != 200: raise Exception(d['message']) bundle_d = unbundle(self.keystore, providing_app.name, d) decrypted_body = bundle_d['data'] # log.info(f'"In the air" decrypted content is: {decrypted_body}') checksum_ok = False # Computationally validate the received data the checksum of the payload data_hash = hashlib.sha224(str( d['payload']).encode('utf-8')).hexdigest() uapp_sc = UnificationUapp(eos_client, requesting_app) data_request = uapp_sc.get_data_request_by_pkey(request_id) und_reward = UndRewards(requesting_app, data_request['price']) if data_request['hash'] == data_hash: print("Data computationally valid") checksum_ok = True json_obj = json.loads(decrypted_body) if 'no-data' not in json_obj and checksum_ok: users_to_pay = json_obj['unification_users'] print("users_to_pay") print(users_to_pay) num_users = len(users_to_pay) print(f"Pay {num_users} users") for username in users_to_pay: print(f'pay {username}') ret = und_reward.send_reward(username, is_user=True, num_users=num_users) log.debug(ret) log.debug(f"Pay provider {providing_app.name}") ret = und_reward.send_reward(providing_app.name, False) log.debug(ret) log.debug(f"Pay Unification") ret = und_reward.pay_unif() log.debug(ret) return self.persist_data(providing_app.name, request_hash, d)
class UnificationDataFactory: def __init__(self, eos_client, uapp_contract_acc, requesting_app, users): """ :param users: A list of users we want to obtain data for. If an empty list is provided, then data for all permissible users are provided. """ self.__uapp_contract_acc = uapp_contract_acc self.__requesting_app = requesting_app self.__haiku_conf = UnificationConfig() self.__my_mother = UnificationMother(eos_client, uapp_contract_acc, get_cleos(), get_ipfs_client()) self.__my_uapp_sc = UnificationUapp(eos_client, uapp_contract_acc) self.__my_lookup = UnificationLookup(default_lookup_db()) self.__users = None if len(users) == 0 else users self.__my_db_schemas = self.__my_uapp_sc.get_all_db_schemas() self.__db_schema_maps = {} self.__granted = [] self.__granted_field_lookup = {} self.__revoked = [] self.__raw_data = None self.__native_user_meta = self.__my_lookup.get_native_user_meta() for pkey, db_schema in self.__my_db_schemas.items(): schema_map = self.__my_lookup.get_schema_map(pkey) self.__db_schema_maps[pkey] = schema_map self.__generate_data() def get_raw_data(self): return self.__raw_data def __build_permissions(self): granted = [] revoked = [] granted_field_lookup = {} pbdb = PermissionBatchDatabase(default_pb_db()) permissions = UnifPermissions(get_ipfs_client(), self.__my_uapp_sc, pbdb) permissions.load_consumer_perms(self.__requesting_app) permission_obj = permissions.get_all_perms() # schema_id = self.__my_db_schemas[0]['pkey'] # tmp - only 1 db schema for user, perms in permission_obj['permissions'].items(): log.debug(perms) schema_perms = perms['0'] # tmp - only 1 db schema if schema_perms['perms'] == '': revoked.append(eosio_account.string_to_name(user)) else: granted.append(eosio_account.string_to_name(user)) native_id = self.__my_lookup.get_native_user_id(user) granted_field_lookup[native_id] = schema_perms['perms'].split( ',') # required granted_field_lookup[native_id].append('account_name') return granted, revoked, granted_field_lookup def __generate_user_list(self): native_user_ids = [] if self.__users is None: for i in self.__granted: native_user_ids.append( self.__my_lookup.get_native_user_id( eosio_account.name_to_string(i))) else: for u in self.__users: user_acc_uint64 = eosio_account.string_to_name(u) if user_acc_uint64 in self.__granted: native_user_ids.append( self.__my_lookup.get_native_user_id(u)) return native_user_ids def __generate_data(self): self.__granted, self.__revoked, self.__granted_field_lookup = self.__build_permissions( ) native_user_ids = self.__generate_user_list() # temporary - there's only 1 db schema per app at the moment sc_schema_pkey = self.__my_db_schemas[0]['pkey'] db_schema = self.__my_db_schemas[0]['schema'] db_schema_map = self.__db_schema_maps[sc_schema_pkey] db_connection = self.__haiku_conf['db_conn']["0"] cols_to_include = [] base64_encode_cols = [] for items in db_schema['fields']: if items['table'] != 'unification_lookup': real_table_data = self.__my_lookup.get_real_table_info( sc_schema_pkey, items['table']) items['table'] = real_table_data['real_table_name'] cols_to_include.append(items['name']) if items['type'] == 'base64_mime_image': base64_encode_cols.append(items['name']) else: real_table_data = self.__my_lookup.get_real_table_info( sc_schema_pkey, 'data_1') items['table'] = real_table_data['real_table_name'] cols_to_include.append(real_table_data['user_id_column']) # temp user_table_info =\ self.__my_lookup.get_real_table_info(sc_schema_pkey, 'users') data_table_info = \ self.__my_lookup.get_real_table_info(sc_schema_pkey, 'data_1') # generate db params for ETL data_source_parms = { 'database': db_schema_map['db_name'], 'filename': db_connection['filename'], 'userTable': user_table_info['real_table_name'], # temp 'dataTable': data_table_info['real_table_name'], # temp 'userIdentifier': user_table_info['user_id_column'], # temp 'dataUserIdentifier': data_table_info['user_id_column'], # temp 'dataColumnsToInclude': cols_to_include, 'native_user_ids': native_user_ids, 'base64_encode_cols': base64_encode_cols, 'providing_app': self.__uapp_contract_acc, 'db_schema': db_schema, 'granted_field_lookup': self.__granted_field_lookup } # grab list of EOS account names if len(native_user_ids) > 0: unification_ids = {} for n_id in native_user_ids: unification_ids[n_id] = self.__my_lookup.get_eos_account(n_id) data_source_parms['unification_id_map'] = unification_ids data_transform_json = TransformDataJSON(data_source_parms) j = data_transform_json.transform() self.__raw_data = j else: d = {"no-data": True} self.__raw_data = json.dumps(d)
def modify_permission(): conf = app.unification_config try: d = flask.request.get_json() eos_perm = d['eos_perm'] req_sender = d['user'] jwt = d['jwt'] cleos = get_cleos() eos_rpc_client = get_eos_rpc_client() # ToDo: find better way to get public key from EOS account public_key = cleos.get_public_key(req_sender, eos_perm) unif_jwt = UnifJWT(jwt, public_key) issuer = unif_jwt.get_issuer() audience = unif_jwt.get_audience() if audience != conf['uapp_contract']: return error_request_not_me() if req_sender != issuer: return error_request_not_you() payload = unif_jwt.get_payload() # Check field list sent matches fields in metadata schema if len(payload['perms']) > 0: field_list = payload['perms'].split(',') uapp_sc = UnificationUapp(eos_rpc_client, conf['uapp_contract']) db_schema = uapp_sc.get_db_schema_by_pkey(int( payload['schema_id'])) if not db_schema: return generic_error( f"Invalid Metadata Schema ID: {payload['schema_id']}") valid_fields = [f['name'] for f in db_schema['schema']['fields']] for pf in field_list: if pf not in valid_fields: return generic_error( f"Invalid field list: {payload['perms']}") batcher = PermissionBatcher(pb_default_db()) rowid = batcher.add_to_queue(issuer, payload['consumer'], payload['schema_id'], payload['perms'], payload['p_nonce'], payload['p_sig'], public_key) d = {'app': conf['uapp_contract'], 'proc_id': rowid} return flask.jsonify(d), 200 except InvalidJWT as e: return invalid_jwt(e) except InvalidPublicKey as e: return invalid_jwt(e) except JWTSignatureMismatch as e: return invalid_jwt(e) except InvalidSignature: return invalid_response() except Exception as e: logger.exception(e) return generic_error()