예제 #1
0
    async def put(self, username):

        if regex.match('^0x[a-fA-F0-9]{40}$', username):

            address_to_update = username

        elif regex.match('^[a-zA-Z][a-zA-Z0-9_]{2,59}$', username):

            async with self.db:
                row = await self.db.fetchrow("SELECT * FROM users WHERE lower(username) = lower($1)", username)
            if row is None:
                raise JSONHTTPError(404, body={'errors': [{'id': 'not_found', 'message': 'Not Found'}]})

            address_to_update = row['token_id']

        else:

            raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_username', 'message': 'Invalid Username'}]})

        request_address = self.verify_request()

        if request_address != address_to_update:

            raise JSONHTTPError(401, body={'errors': [{'id': 'permission_denied', 'message': 'Permission Denied'}]})

        return await self.update_user(address_to_update)
예제 #2
0
    async def get(self):

        try:
            offset = int(self.get_query_argument('offset', 0))
            limit = int(self.get_query_argument('limit', 10))
        except ValueError:
            raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Bad Arguments'}]})

        query = self.get_query_argument('query', None)
        apps = parse_boolean(self.get_query_argument('apps', None))
        payment_address = self.get_query_argument('payment_address', None)
        if payment_address and not validate_address(payment_address):
            raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Invalid payment_address'}]})

        if query is None:
            if payment_address:
                async with self.db:
                    rows = await self.db.fetch(
                        "SELECT * FROM users WHERE payment_address = $3 "
                        "ORDER BY payment_address, name, username "
                        "OFFSET $1 LIMIT $2",
                        offset, limit, payment_address)
                results = [user_row_for_json(self.request, row) for row in rows]
            else:
                results = []
        else:
            # strip punctuation
            query = ''.join([c for c in query if c not in string.punctuation])
            # split words and add in partial matching flags
            query = '|'.join(['{}:*'.format(word) for word in query.split(' ') if word])
            args = [offset, limit, query]
            where_q = []
            if payment_address:
                where_q.append("payment_address = ${}".format(len(args) + 1))
                args.append(payment_address)
            if apps is not None:
                where_q.append("is_app = ${}".format(len(args) + 1))
                args.append(apps)
            where_q = " AND {}".format(" AND ".join(where_q)) if where_q else ""
            sql = ("SELECT * FROM "
                   "(SELECT * FROM users, TO_TSQUERY($3) AS q "
                   "WHERE (tsv @@ q){}) AS t1 "
                   "ORDER BY TS_RANK_CD(t1.tsv, TO_TSQUERY($3)) DESC, name, username "
                   "OFFSET $1 LIMIT $2"
                   .format(where_q))
            async with self.db:
                rows = await self.db.fetch(sql, *args)
            results = [user_row_for_json(self.request, row) for row in rows]
        querystring = 'query={}'.format(query)
        if apps is not None:
            querystring += '&apps={}'.format('true' if apps else 'false')
        if payment_address:
            querystring += '&payment_address={}'.format(payment_address)

        self.write({
            'query': querystring,
            'offset': offset,
            'limit': limit,
            'results': results
        })
예제 #3
0
    async def get(self, username):

        # check if ethereum address is given
        if regex.match('^0x[a-fA-F0-9]{40}$', username):

            sql = "SELECT * FROM users WHERE token_id = $1"
            args = [username]

        # otherwise verify that username is valid
        elif not regex.match('^[a-zA-Z][a-zA-Z0-9_]{2,59}$', username):
            raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_username', 'message': 'Invalid Username'}]})

        else:

            sql = "SELECT * FROM users WHERE lower(username) = lower($1)"
            args = [username]

        if self.apps_only:
            sql += " AND is_app = $2 AND blocked = $3"
            args.extend([True, False])

        async with self.db:
            row = await self.db.fetchrow(sql, *args)

        if row is None:
            raise JSONHTTPError(404, body={'errors': [{'id': 'not_found', 'message': 'Not Found'}]})

        self.write(user_row_for_json(self.request, row))
예제 #4
0
    async def post(self):

        if self.is_request_signed():
            sender_token_id = self.verify_request()
        else:
            # this is an anonymous transaction
            sender_token_id = None

        try:
            result = await TokenEthJsonRPC(
                sender_token_id,
                self.application).send_transaction(**self.json)
        except JsonRPCInternalError as e:
            raise JSONHTTPError(500, body={'errors': [e.data]})
        except JsonRPCError as e:
            raise JSONHTTPError(400, body={'errors': [e.data]})
        except TypeError:
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        self.write({"tx_hash": result})
예제 #5
0
    async def update_user_avatar(self, address):

        # make sure a user with the given address exists
        async with self.db:
            user = await self.db.fetchrow("SELECT * FROM users WHERE token_id = $1", address)

        if user is None:
            raise JSONHTTPError(404, body={'errors': [{'id': 'not_found', 'message': 'Not Found'}]})

        files = self.request.files.values()
        if len(files) != 1:
            raise JSONHTTPError(404, body={'errors': [{'id': 'bad_arguments', 'message': 'Too many files'}]})

        file = next(iter(files))
        if len(file) != 1:
            raise JSONHTTPError(404, body={'errors': [{'id': 'bad_arguments', 'message': 'Too many files'}]})
        data = file[0]['body']
        mime_type = file[0]['content_type']

        data, cache_hash, format = await self.run_in_executor(process_image, data, mime_type)

        async with self.db:
            await self.db.execute("INSERT INTO avatars (token_id, img, hash, format) VALUES ($1, $2, $3, $4) "
                                  "ON CONFLICT (token_id) DO UPDATE "
                                  "SET img = EXCLUDED.img, hash = EXCLUDED.hash, format = EXCLUDED.format, last_modified = (now() AT TIME ZONE 'utc')",
                                  address, data, cache_hash, format)
            avatar_url = "/avatar/{}.{}".format(address, 'jpg' if format == 'JPEG' else 'png')
            await self.db.execute("UPDATE users SET avatar = $1 WHERE token_id = $2", avatar_url, address)
            user = await self.db.fetchrow("SELECT * FROM users WHERE token_id = $1", address)
            await self.db.commit()

        self.write(user_row_for_json(self.request, user))
예제 #6
0
    async def get(self, tx_hash):

        format = self.get_query_argument('format', 'rpc')

        try:
            tx = await TokenEthJsonRPC(
                None, self.application).get_transaction(tx_hash)
        except JsonRPCError as e:
            raise JSONHTTPError(400, body={'errors': [e.data]})

        if tx is None:
            raise JSONHTTPError(
                404,
                body={'error': [{
                    'id': 'not_found',
                    'message': 'Not Found'
                }]})

        if format.lower() == 'sofa':

            async with self.db:
                row = await self.db.fetchrow(
                    "SELECT * FROM transactions where transaction_hash = $1",
                    tx_hash)
            if row is not None and row['error'] is not None:
                tx['error'] = row['error']
            payment = SofaPayment.from_transaction(tx)
            message = payment.render()
            self.set_header('Content-Type', 'text/plain')
            self.write(message.encode('utf-8'))

        else:

            self.write(tx)
예제 #7
0
    async def post(self):

        token_id = self.verify_request()
        payload = self.json

        if 'addresses' not in payload or len(payload['addresses']) == 0:
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        addresses = payload['addresses']

        try:
            await TokenEthJsonRPC(token_id,
                                  self.application).unsubscribe(*addresses)
        except JsonRPCError as e:
            raise JSONHTTPError(400, body={'errors': [e.data]})
        except TypeError:
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        self.set_status(204)
예제 #8
0
    async def get(self, key):

        if self.is_request_signed():

            address = self.verify_request()
            self.set_login_result(key, address)
            self.set_status(204)

        else:

            if key not in self.login_requests:
                self.create_new_login_future(key)

            address = await self.login_requests[key]

            if address is None:
                raise JSONHTTPError(400, body={'errors': [{'id': 'request_timeout', 'message': 'Login request timed out'}]})
            if address is False:
                raise JSONHTTPError(401, body={'errors': [{'id': 'login_failed', 'message': 'Login failed'}]})

            if hasattr(self, 'on_login'):
                f = self.on_login(address)
                if asyncio.iscoroutine(f):
                    f = await f
                return f
            # else
            self.write({"address": address})
예제 #9
0
def process_image(data, mime_type):
    stream = io.BytesIO(data)
    try:
        img = Image.open(stream)
    except OSError:
        raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Invalid image data'}]})

    if mime_type == 'image/jpeg' and img.format == 'JPEG':
        format = "JPEG"
        subsampling = 'keep'
        # check exif information for orientation
        if hasattr(img, '_getexif'):
            x = img._getexif()
            if x and EXIF_ORIENTATION in x and x[EXIF_ORIENTATION] > 1 and x[EXIF_ORIENTATION] < 9:
                orientation = x[EXIF_ORIENTATION]
                subsampling = get_sampling(img)
                if orientation == 2:
                    # Vertical Mirror
                    img = img.transpose(Image.FLIP_LEFT_RIGHT)
                elif orientation == 3:
                    # Rotation 180°
                    img = img.transpose(Image.ROTATE_180)
                elif orientation == 4:
                    # Horizontal Im
                    img = img.transpose(Image.FLIP_TOP_BOTTOM)
                elif orientation == 5:
                    # Horizontal Im + Rotation 90° CCW
                    img = img.transpose(Image.FLIP_TOP_BOTTOM).transpose(Image.ROTATE_90)
                elif orientation == 6:
                    # Rotation 270°
                    img = img.transpose(Image.ROTATE_270)
                elif orientation == 7:
                    # Horizontal Im + Rotation 270°
                    img = img.transpose(Image.FLIP_TOP_BOTTOM).transpose(Image.ROTATE_270)
                elif orientation == 8:
                    # Rotation 90°
                    img = img.transpose(Image.ROTATE_90)
        save_kwargs = {'subsampling': subsampling, 'quality': 85}
    elif mime_type == 'image/png' and img.format == 'PNG':
        format = "PNG"
        save_kwargs = {'icc_profile': img.info.get("icc_profile")}
    else:
        raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Unsupported image format'}]})

    if img.size[0] > 512 or img.size[1] > 512:
        img.thumbnail((512, 512))

    stream = io.BytesIO()
    img.save(stream, format=format, optimize=True, **save_kwargs)

    data = stream.getbuffer().tobytes()
    hasher = hashlib.md5()
    hasher.update(data)
    cache_hash = hasher.hexdigest()

    return data, cache_hash, format
예제 #10
0
    async def post(self, service):

        token_id = self.verify_request()
        payload = self.json

        if 'registration_id' not in payload:
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        # TODO: registration id verification

        async with self.db:

            await self.db.execute(
                "DELETE FROM push_notification_registrations WHERE service = $1 AND registration_id = $2 AND token_id = $3",
                service, payload['registration_id'], token_id)

            await self.db.commit()

        self.set_status(204)
예제 #11
0
    async def post(self, service):

        token_id = self.verify_request()
        payload = self.json

        if 'registration_id' not in payload:
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        # TODO: registration id verification

        async with self.db:

            await self.db.execute(
                "INSERT INTO push_notification_registrations (service, registration_id, token_id) "
                "VALUES ($1, $2, $3) ON CONFLICT (service, registration_id) DO UPDATE SET token_id = $3",
                service, payload['registration_id'], token_id)

            await self.db.commit()

        self.set_status(204)
예제 #12
0
    async def post(self):
        if 'auth_token' not in self.json:
            raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Bad Arguments'}]})
        token = self.json['auth_token']

        idclient = IdServiceClient(use_tornado=True)
        try:
            user = await idclient.whodis(token)
        except:
            log.exception("...")
            user = None

        if user:
            self.set_secure_cookie("user", user['token_id'])
            self.set_status(204)
        else:
            raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_token', 'message': 'Invalid token'}]})
예제 #13
0
    async def get(self):
        address = self.current_user
        if address:
            idclient = IdServiceClient(use_tornado=True)
            user = await idclient.get_user(address)
        else:
            raise JSONHTTPError(401)

        self.write({"user": user})
예제 #14
0
    async def get(self):

        token_id = self.verify_request()
        try:
            result = await TokenEthJsonRPC(
                token_id, self.application).list_subscriptions()
        except JsonRPCError as e:
            raise JSONHTTPError(400, body={'errors': [e.data]})
        except TypeError:
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        self.write({"subscriptions": result})
예제 #15
0
    async def get(self, address):

        try:
            result = await TokenEthJsonRPC(
                None, self.application).get_balance(address)
        except JsonRPCError as e:
            raise JSONHTTPError(400, body={'errors': [e.data]})

        self.write(result)
예제 #16
0
    def put(self):
        address = self.verify_request()

        if self.request.headers['Content-Type'] != 'application/json' and not self.request.files:
            raise JSONHTTPError(400, body={'errors': [{'id': 'bad_data', 'message': 'Expected application/json or multipart/form-data'}]})

        if self.request.files:
            return self.update_user_avatar(address)
        else:
            return self.update_user(address)
예제 #17
0
    async def get(self):

        if not await self.is_admin_user():
            raise JSONHTTPError(401)

        query = self.get_query_argument('query')

        client = IdServiceClient(use_tornado=True)
        results = await client.search_user(query)

        self.write(results)
예제 #18
0
    async def post(self):
        if not self.current_user:
            raise JSONHTTPError(401)

        address = self.json['address']
        async with self.db:
            await self.db.execute(
                "UPDATE submissions SET request_for_featured = TRUE WHERE app_token_id = $1",
                address)
            await self.db.commit()

        self.set_status(204)
예제 #19
0
    async def get(self, token_id):

        async with self.db:
            row = await self.db.fetchrow(
                "SELECT apps.*, sofa_manifests.* FROM apps "
                "JOIN sofa_manifests ON "
                "sofa_manifests.token_id = apps.token_id "
                "WHERE apps.token_id = $1", token_id)
        if row is None:
            raise JSONHTTPError(404, body={'errors': [{'id': 'not_found', 'message': 'Not Found'}]})
        result = app_from_row(row)
        self.write(result)
예제 #20
0
    async def post(self):
        if not await self.is_admin_user():
            raise JSONHTTPError(401)

        address = self.json['address']
        async with self.db:
            await self.db.execute(
                "UPDATE apps SET featured = FALSE WHERE token_id = $1",
                address)
            await self.db.commit()

        self.set_status(204)
예제 #21
0
    async def post(self):

        if not await self.is_admin_user():
            raise JSONHTTPError(401)

        address = self.json['address']
        async with self.db:
            await self.db.execute("DELETE FROM admins WHERE token_id= $1",
                                  address)
            await self.db.commit()

        self.set_status(204)
예제 #22
0
    async def get(self):
        if not self.current_user:
            raise JSONHTTPError(401)

        try:
            offset = int(self.get_query_argument('offset', 0))
            limit = int(self.get_query_argument('limit', 10))
        except ValueError:
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        async with self.db:
            count = await self.db.fetchrow(
                "SELECT count(*) FROM submissions WHERE submitter_token_id = $1",
                self.current_user)
            apps = await self.db.fetch(
                "SELECT apps.*, sofa_manifests.*, submissions.request_for_featured FROM submissions "
                "JOIN apps ON submissions.app_token_id = apps.token_id "
                "JOIN sofa_manifests ON apps.token_id = sofa_manifests.token_id "
                "WHERE submissions.submitter_token_id = $1 "
                "ORDER BY apps.name "
                "OFFSET $2 "
                "LIMIT $3", self.current_user, offset, limit)

        results = []
        for row in apps:
            val = response_for_row(row)
            results.append(val)

        self.write({
            'offset': offset,
            'limit': limit,
            'apps': results,
            'total': count['count']
        })
예제 #23
0
    async def post(self):

        try:
            if 'from' in self.json:
                self.json['from_address'] = self.json.pop('from')
            if 'to' in self.json:
                self.json['to_address'] = self.json.pop('to')
            result = await TokenEthJsonRPC(
                None,
                self.application).create_transaction_skeleton(**self.json)
        except JsonRPCError as e:
            raise JSONHTTPError(400, body={'errors': [e.data]})
        except TypeError:
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        self.write({"tx": result})
예제 #24
0
    async def on_login(self, address):

        num = int(data_encoder(os.urandom(16))[2:], 16)
        token = b62encode(num)

        async with self.db:
            row = await self.db.fetchrow("SELECT * FROM users where token_id = $1", address)
            if row is None:
                raise JSONHTTPError(401)
            await self.db.execute("INSERT INTO auth_tokens (token, address) VALUES ($1, $2)",
                                  token, address)
            await self.db.commit()
        self.write({'auth_token': token})
예제 #25
0
    async def get(self):
        if not await self.is_admin_user():
            raise JSONHTTPError(401)

        try:
            offset = int(self.get_query_argument('offset', 0))
            limit = int(self.get_query_argument('limit', 10))
        except ValueError:
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        async with self.db:
            count = await self.db.fetchrow("SELECT count(*) FROM admins")
            admins = await self.db.fetch(
                "SELECT * FROM admins "
                "OFFSET $1 "
                "LIMIT $2", offset, limit)

        results = []
        client = IdServiceClient(use_tornado=True)
        for row in admins:
            try:
                val = await client.get_user(row['token_id'])
                results.append(val)
            except HTTPError:
                pass

        self.write({
            'offset': offset,
            'limit': limit,
            'admins': results,
            'total': count['count']
        })
예제 #26
0
    async def post(self):

        if 'reputation' not in self.application.config or 'id' not in self.application.config['reputation']:
            raise HTTPError(404)

        try:
            address = self.verify_request()
        except JSONHTTPError:
            raise HTTPError(404)

        if address != self.application.config['reputation']['id']:
            raise HTTPError(404)

        if not all(x in self.json for x in ['address', 'score', 'count']):
            raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Bad Arguments'}]})

        token_id = self.json['address']
        if not validate_address(token_id):
            raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_address', 'message': 'Invalid Address'}]})

        count = self.json['count']
        count = parse_int(count)
        if count is None:
            raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_count', 'message': 'Invalid Count'}]})

        score = self.json['score']
        if isinstance(score, str) and validate_decimal_string(score):
            score = Decimal(score)
        if not isinstance(score, (int, float, Decimal)):
            raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_score', 'message': 'Invalid Score'}]})

        async with self.db:
            await self.db.execute("UPDATE apps SET reputation_score = $1, review_count = $2 WHERE token_id = $3",
                                  score, count, token_id)
            await self.db.commit()

        self.set_status(204)
예제 #27
0
    async def post(self):

        if not await self.is_admin_user():
            raise JSONHTTPError(401)

        address = self.json['address']
        async with self.db:
            row = await self.db.fetchrow(
                "SELECT * FROM admins WHERE token_id = $1", address)
            if row is None:
                await self.db.execute(
                    "INSERT INTO admins (token_id) VALUES ($1)", address)
                await self.db.commit()

        self.set_status(204)
예제 #28
0
    async def get(self, force_featured=None):
        try:
            offset = int(self.get_query_argument('offset', 0))
            limit = int(self.get_query_argument('limit', 10))
        except ValueError:
            raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Bad Arguments'}]})

        if force_featured:
            featured = True
        else:
            featured = self.get_query_argument('featured', 'false')
            if featured.lower() == 'false':
                featured = False
            else:
                featured = True
        query = self.get_query_argument('query', None)

        args = []
        sql = "SELECT * FROM apps JOIN sofa_manifests ON sofa_manifests.token_id = apps.token_id "
        if query:
            # strip any punctuation
            query = ''.join([c for c in query if c not in string.punctuation])
            args.append('%{}%'.format(query))
            sql += " WHERE apps.name ILIKE $1"
            if featured:
                sql += " AND apps.featured IS TRUE"
        elif featured:
            sql += " WHERE apps.featured IS TRUE"
        countsql = "SELECT COUNT(*) " + sql[8:]
        countargs = args[:]
        sql += " ORDER BY apps.name OFFSET ${} LIMIT ${}".format(len(args) + 1, len(args) + 2)
        args.extend([offset, limit])

        async with self.db:
            count = await self.db.fetchrow(countsql, *countargs)
            rows = await self.db.fetch(sql, *args)

        results = [app_from_row(row) for row in rows]

        self.write({
            'query': query or '',
            'offset': offset,
            'limit': limit,
            'apps': results,
            'featured': featured,
            'total': count['count']
        })
예제 #29
0
    async def get(self, token):

        user = None
        async with self.db:
            row = await self.db.fetchrow("SELECT u.*, a.created AS auth_token_created FROM auth_tokens a "
                                         "JOIN users u ON a.address = u.token_id "
                                         "WHERE a.token = $1", token)
            if row is not None:
                # only allow tokens to be used for 10 minutes
                print(row)
                if row['auth_token_created'] + timedelta(minutes=10) > datetime.utcnow():
                    user = user_row_for_json(self.request, row)
                else:
                    print('expired')
                # remove token after single use
                await self.db.execute("DELETE FROM auth_tokens WHERE token = $1", token)
                await self.db.commit()
            else:
                print('not found')

        if user:
            self.write(user)
        else:
            raise JSONHTTPError(404)
예제 #30
0
    async def post(self):
        """Handles submitting a new app to the directory service"""

        if not self.current_user:
            raise JSONHTTPError(401)

        if not all(x in self.json for x in ['display_name', 'token_id']):
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        token_id = self.json['token_id']
        if not validate_address(token_id):
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'invalid_token_id',
                                        'message': 'Invalid Arguments'
                                    }]
                                })

        # check if the user has already submitted this app
        async with self.db:
            existing = await self.db.fetchrow(
                "SELECT * FROM submissions WHERE submitter_token_id = $1 AND app_token_id = $2",
                self.current_user, self.json['token_id'])
        if existing:
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'already_exists',
                                        'message': 'App already exists'
                                    }]
                                })

        # check if the app has already been submitted (by someone else for e.g.)
        async with self.db:
            existing = await self.db.fetchrow(
                "SELECT * FROM apps WHERE token_id = $1",
                self.json['token_id'])

        # TODO: maybe this is actually ok (but it would be weird)
        if existing:
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'already_exists',
                                        'message': 'App already exists'
                                    }]
                                })

        client = IdServiceClient(use_tornado=True)
        bot = await client.get_user(self.json['token_id'])
        if bot is None:
            raise JSONHTTPError(
                400,
                body={
                    'errors': [{
                        'id':
                        'not_found',
                        'message':
                        'Cannot find given address in the id service'
                    }]
                })

        username = bot['username']
        payment_address = bot['payment_address']
        display_name = self.json['display_name']

        init_request = ['paymentAddress', 'language']
        languages = ['en']
        interfaces = ['ChatBot']
        protocol = 'sofa-v1.0'

        avatar_url = self.json.get('avatar_url', None)
        if not avatar_url:
            avatar_url = 'https://token-id-service.herokuapp.com/identicon/{}.png'.format(
                token_id)

        async with self.db:
            await self.db.execute(
                "INSERT INTO apps "
                "(token_id, name) VALUES ($1, $2) ", token_id, display_name)
            await self.db.execute(
                "INSERT INTO sofa_manifests "
                "(token_id, payment_address, username, init_request, languages, interfaces, protocol, avatar_url) "
                "VALUES "
                "($1, $2, $3, $4, $5, $6, $7, $8)", token_id, payment_address,
                username, init_request, languages, interfaces, protocol,
                avatar_url)
            await self.db.execute(
                "INSERT INTO submissions "
                "(app_token_id, submitter_token_id) "
                "VALUES "
                "($1, $2)", token_id, self.current_user)
            row = await self.db.fetchrow(
                "SELECT * FROM apps JOIN sofa_manifests ON apps.token_id = sofa_manifests.token_id WHERE apps.token_id = $1",
                token_id)
            await self.db.commit()

        self.write(response_for_row(row))