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 })
def unsubscribe(self, *addresses): for address in addresses: if not validate_address(address): raise JsonRPCInvalidParamsError(data={'id': 'bad_arguments', 'message': 'Bad Arguments'}) self.request_handler.unsubscribe(addresses) return True
async def get_balance(self, address): if not validate_address(address): raise JsonRPCInvalidParamsError(data={ 'id': 'invalid_address', 'message': 'Invalid Address' }) confirmed, unconfirmed = await self.get_balances(address) return { "confirmed_balance": hex(confirmed), "unconfirmed_balance": hex(unconfirmed) }
def test_validate_address(self): self.assertTrue( utils.validate_address( "0x056db290f8ba3250ca64a45d16284d04bc6f5fbf")) self.assertTrue( utils.validate_address( u"0x056db290f8ba3250ca64a45d16284d04bc6f5fbf")) self.assertFalse(utils.validate_address("hello")) self.assertFalse(utils.validate_address("0x12345")) self.assertFalse(utils.validate_address(None)) self.assertFalse(utils.validate_address({})) self.assertFalse( utils.validate_address(0x056db290f8ba3250ca64a45d16284d04bc6f5fbf)) self.assertFalse( utils.validate_address( "0x114655db4898a6580f0abfc53fc0c0a88110724abf8d41f2abf206c69d7d4c821ed2cdf6939484ef6aebc39ce5662363b82140106bbc374a0f1381b6948214b001" ))
async def unsubscribe(self, *addresses): for address in addresses: if not validate_address(address): raise JsonRPCInvalidParamsError(data={ 'id': 'bad_arguments', 'message': 'Bad Arguments' }) async with self.db: await self.db.execute( "DELETE FROM notification_registrations WHERE token_id = $1 AND ({})" .format(' OR '.join('eth_address = ${}'.format(i + 2) for i, _ in enumerate(addresses))), self.user_token_id, *addresses) await self.db.commit() return True
async def subscribe(self, *addresses): insert_args = [] for address in addresses: if not validate_address(address): raise JsonRPCInvalidParamsError(data={ 'id': 'bad_arguments', 'message': 'Bad Arguments' }) insert_args.extend([self.user_token_id, address]) async with self.db: await self.db.execute( "INSERT INTO notification_registrations VALUES {} ON CONFLICT DO NOTHING" .format(', '.join('(${}, ${})'.format((i * 2) + 1, (i * 2) + 2) for i, _ in enumerate(addresses))), *insert_args) await self.db.commit() return True
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)
async def create_transaction_skeleton(self, *, to_address, from_address, value=0, nonce=None, gas=None, gas_price=None, data=None): if not validate_address(from_address): raise JsonRPCInvalidParamsError(data={ 'id': 'invalid_from_address', 'message': 'Invalid From Address' }) if not validate_address(to_address): raise JsonRPCInvalidParamsError(data={ 'id': 'invalid_to_address', 'message': 'Invalid To Address' }) if value: value = parse_int(value) if value is None: raise JsonRPCInvalidParamsError(data={ 'id': 'invalid_value', 'message': 'Invalid Value' }) # check optional arguments if nonce is None: # check cache for nonce nonce = self.redis.get("nonce:{}".format(from_address)) if nonce: nonce = int(nonce) # get the network's value too nw_nonce = await self.eth.eth_getTransactionCount(from_address) if nonce is None or nw_nonce > nonce: # if not cached, or the cached value is lower than # the network value, use the network value! nonce = nw_nonce else: nonce = parse_int(nonce) if nonce is None: raise JsonRPCInvalidParamsError(data={ 'id': 'invalid_nonce', 'message': 'Invalid Nonce' }) if data is not None: if isinstance(data, int): data = hex(data) if isinstance(data, str): try: data = data_decoder(data) except binascii.Error: pass if not isinstance(data, bytes): raise JsonRPCInvalidParamsError(data={ 'id': 'invalid_data', 'message': 'Invalid Data field' }) else: data = b'' if gas is None: # if there is data the default startgas value wont be enough if data: gas = await self.eth.eth_estimateGas(from_address, to_address, nonce=nonce, data=data) else: gas = DEFAULT_STARTGAS else: gas = parse_int(gas) if gas is None: raise JsonRPCInvalidParamsError(data={ 'id': 'invalid_gas', 'message': 'Invalid Gas' }) if gas_price is None: gas_price = DEFAULT_GASPRICE else: gas_price = parse_int(gas_price) if gas_price is None: raise JsonRPCInvalidParamsError(data={ 'id': 'invalid_gas_price', 'message': 'Invalid Gas Price' }) tx = create_transaction(nonce=nonce, gasprice=gas_price, startgas=gas, to=to_address, value=value, data=data) transaction = encode_transaction(tx) return transaction
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))
async def put(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_address', '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 not existing: raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'app_does_not_exists', 'message': 'App Doesn\'t exists' }] }) display_name = self.json['display_name'] 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( "UPDATE apps " "SET name = $1 " "WHERE token_id = $2", display_name, token_id) await self.db.execute( "UPDATE sofa_manifests " "SET avatar_url = $1 " "WHERE token_id = $2", avatar_url, token_id) row = await self.db.fetchrow( "SELECT * FROM apps " "JOIN sofa_manifests ON apps.token_id = sofa_manifests.token_id " "JOIN submissions ON submissions.app_token_id = apps.token_id " "WHERE apps.token_id = $1", token_id) await self.db.commit() self.write(response_for_row(row))
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'}]}) query = self.get_query_argument('query', 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 force_featured: featured = True else: featured = self.get_query_argument('featured', 'false') if featured.lower() == 'false': featured = False else: featured = True if query is None: where_q = [] order_by = [] args = [offset, limit] if payment_address: where_q.append("payment_address = ${}".format(len(args) + 1)) args.append(payment_address) order_by.append(payment_address) if featured: where_q.append("featured = ${}".format(len(args) + 1)) args.append(True) where_q.append("blocked = ${}".format(len(args) + 1)) args.append(False) where_q.append("is_app = ${}".format(len(args) + 1)) args.append(True) order_by.extend(['name', 'username']) async with self.db: sql = ("SELECT * FROM users WHERE {} " "ORDER BY {} " "OFFSET $1 LIMIT $2".format(" AND ".join(where_q), ", ".join(order_by))) rows = await self.db.fetch( sql, *args) results = [user_row_for_json(self.request, row) for row in rows] 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 featured: where_q.append("featured = ${}".format(len(args) + 1)) args.append(True) where_q.append("is_app = ${}".format(len(args) + 1)) args.append(True) where_q.append("blocked = ${}".format(len(args) + 1)) args.append(False) where_q.append("is_app = ${}".format(len(args) + 1)) args.append(True) 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 payment_address: querystring += '&payment_address={}'.format(payment_address) self.write({ 'query': querystring, 'offset': offset, 'limit': limit, 'results': results })
async def post(self): address = self.verify_request() payload = self.json # check if the address has already registered a username async with self.db: row = await self.db.fetchrow("SELECT * FROM users WHERE token_id = $1", address) if row is not None: raise JSONHTTPError(400, body={'errors': [{'id': 'already_registered', 'message': 'The provided token id address is already registered'}]}) if 'username' in payload: username = payload['username'] if not validate_username(username): raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_username', 'message': 'Invalid Username'}]}) # check username doesn't already exist async with self.db: row = await self.db.fetchrow("SELECT * FROM users WHERE lower(username) = lower($1)", username) if row is not None: raise JSONHTTPError(400, body={'errors': [{'id': 'username_taken', 'message': 'Username Taken'}]}) else: # generate temporary username for i in itertools.count(): username = generate_username(MIN_AUTOID_LENGTH + i) async with self.db: row = await self.db.fetchrow("SELECT * FROM users WHERE lower(username) = lower($1)", username) if row is None: break if 'payment_address' in payload: payment_address = payload['payment_address'] if not validate_address(payment_address): raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_payment_address', 'message': 'Invalid Payment Address'}]}) else: #raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Missing Payment Address'}]}) # TODO: not required right now payment_address = None if 'is_app' in payload: is_app = parse_boolean(payload['is_app']) if is_app is None: raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Bad Arguments'}]}) else: is_app = False if 'avatar' in payload: avatar = payload['avatar'] if not isinstance(avatar, str): raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Bad Arguments'}]}) else: avatar = None if 'name' in payload: name = payload['name'] if not isinstance(name, str): raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Bad Arguments'}]}) else: name = None if 'about' in payload: about = payload['about'] if not isinstance(about, str): raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Bad Arguments'}]}) else: about = None if 'location' in payload: location = payload['location'] if not isinstance(location, str): raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Bad Arguments'}]}) else: location = None async with self.db: await self.db.execute("INSERT INTO users " "(username, token_id, payment_address, name, avatar, is_app, about, location) " "VALUES " "($1, $2, $3, $4, $5, $6, $7, $8)", username, address, payment_address, name, avatar, is_app, about, location) 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))
async def update_user(self, address): try: payload = self.json except: raise JSONHTTPError(400, body={'errors': [{'id': 'bad_data', 'message': 'Error decoding data. Expected JSON content'}]}) async with self.db: # make sure a user with the given address exists 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'}]}) # backwards compat if 'custom' in payload: custom = payload.pop('custom') if 'name' in custom: payload['name'] = custom['name'] if 'avatar' in custom: payload['avatar'] = custom['avatar'] if 'about' in custom: payload['about'] = custom['about'] if 'location' in custom: payload['location'] = custom['location'] if not any(x in payload for x in ['username', 'about', 'name', 'avatar', 'payment_address', 'is_app', 'location']): raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Bad Arguments'}]}) if 'username' in payload and user['username'] != payload['username']: username = payload['username'] if not validate_username(username): raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_username', 'message': 'Invalid Username'}]}) # make sure the username isn't used by a different user row = await self.db.fetchrow("SELECT * FROM users WHERE lower(username) = lower($1)", username) if row is not None: raise JSONHTTPError(400, body={'errors': [{'id': 'username_taken', 'message': 'Username Taken'}]}) await self.db.execute("UPDATE users SET username = $1 WHERE token_id = $2", username, address) if 'payment_address' in payload and payload['payment_address'] != user['payment_address']: payment_address = payload['payment_address'] if not validate_address(payment_address): raise JSONHTTPError(400, body={'errors': [{'id': 'invalid_payment_address', 'message': 'Invalid Payment Address'}]}) await self.db.execute("UPDATE users SET payment_address = $1 WHERE token_id = $2", payment_address, address) if 'is_app' in payload and payload['is_app'] != user['is_app']: is_app = parse_boolean(payload['is_app']) if not isinstance(is_app, bool): raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Bad Arguments'}]}) await self.db.execute("UPDATE users SET is_app = $1 WHERE token_id = $2", is_app, address) if 'name' in payload and payload['name'] != user['name']: name = payload['name'] if not isinstance(name, str): raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Invalid Name'}]}) await self.db.execute("UPDATE users SET name = $1 WHERE token_id = $2", name, address) if 'avatar' in payload and payload['avatar'] != user['avatar']: avatar = payload['avatar'] if not isinstance(avatar, str): raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Invalid Avatar'}]}) await self.db.execute("UPDATE users SET avatar = $1 WHERE token_id = $2", avatar, address) if 'about' in payload and payload['about'] != user['about']: about = payload['about'] if not isinstance(about, str): raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Invalid About'}]}) await self.db.execute("UPDATE users SET about = $1 WHERE token_id = $2", about, address) if 'location' in payload and payload['location'] != user['location']: location = payload['location'] if not isinstance(location, str): raise JSONHTTPError(400, body={'errors': [{'id': 'bad_arguments', 'message': 'Invalid Location'}]}) await self.db.execute("UPDATE users SET location = $1 WHERE token_id = $2", location, 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))
def do_POST(self): # TODO: figure out why read is blocking here data = self.rfile.read(len(self.rfile.peek())) data = data.decode('utf-8') data = json.loads(data) if self.path == "/v1/tx/skel": gas_price = parse_int( data['gas_price']) if 'gas_price' in data else DEFAULT_GASPRICE gas = parse_int(data['gas']) if 'gas' in data else DEFAULT_STARTGAS nonce = parse_int(data['nonce']) if 'nonce' in data else 0 if 'value' not in data or 'from' not in data or 'to' not in data: self.write_data( 400, { 'errors': [{ 'id': 'bad_arguments', 'message': 'Bad Arguments' }] }) return value = parse_int(data['value']) to_address = data['to'] from_address = data['from'] if not validate_address(to_address): self.write_data( 400, { 'errors': [{ 'id': 'invalid_to_address', 'message': 'Invalid To Address' }] }) return if not validate_address(from_address): self.write_data( 400, { 'errors': [{ 'id': 'invalid_from_address', 'message': 'Invalid From Address' }] }) return tx = create_transaction(nonce=nonce, gasprice=gas_price, startgas=gas, to=to_address, value=value) transaction = encode_transaction(tx) self.write_data( 200, { "tx_data": { "nonce": hex(nonce), "from": from_address, "to": to_address, "value": hex(value), "startGas": hex(gas), "gasPrice": hex(gas_price) }, "tx": transaction }) elif self.path == "/v1/tx": tx = decode_transaction(data['tx']) if 'signature' in data: sig = data_decoder(data['signature']) add_signature_to_transaction(tx, sig) self.write_data(200, {"tx_hash": data_encoder(tx.hash)}) else: self.write_data(404)