async def post(self): try: # normalize inputs 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') elif 'to_address' not in self.json: self.json['to_address'] = None # the following are to deal with different representations # of the same concept from different places if 'gasPrice' in self.json: self.json['gas_price'] = self.json.pop('gasPrice') if 'gasprice' in self.json: self.json['gas_price'] = self.json.pop('gasprice') if 'startgas' in self.json: self.json['gas'] = self.json.pop('startgas') result = await ToshiEthJsonRPC( None, self.application, self.request).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})
async def post(self): self.set_header("Access-Control-Allow-Origin", "*") self.set_header("Access-Control-Allow-Headers", "x-requested-with") self.set_header('Access-Control-Allow-Methods', 'POST') if 'tx_hash' not in self.json or 'signature' not in self.json: raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'bad_arguments', 'message': 'Bad Arguments' }] }) tx_hash = self.json['tx_hash'] signature = self.json['signature'] try: await ToshiEthJsonRPC(None, self.application, self.request).cancel_queued_transaction( tx_hash, signature) except JsonRPCError as e: raise JSONHTTPError(400, body={'errors': [e.data]}) self.set_status(204)
async def post(self): if self.is_request_signed(): sender_toshi_id = self.verify_request() else: # this is an anonymous transaction sender_toshi_id = None try: result = await ToshiEthJsonRPC( sender_toshi_id, self.application, self.request).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})
async def get(self, dapp_id): try: dapp_id = int(dapp_id) except ValueError: raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'invalid_dapp_id', 'message': 'Invalid Dapp Id' }] }) async with self.db: dapp = await self.db.fetchrow( "SELECT d.*, JSONB_OBJECT_AGG(cat.category_id, cat.name) AS category_map, ARRAY_AGG(cat.category_id) as categories " "FROM dapps d " "JOIN dapp_categories dc ON d.dapp_id = dc.dapp_id " "JOIN categories cat ON dc.category_id = cat.category_id " "WHERE d.dapp_id = $1 " "GROUP BY d.dapp_id", dapp_id) if not dapp: raise JSONHTTPError(404, body={ 'errors': [{ 'id': 'invalid_dapp_id', 'message': 'Invalid Dapp Id' }] }) dapp_json = map_dapp_json(dapp) self.write({ 'dapp': dapp_json, 'categories': json_decode(dapp['category_map']) })
async def post(self): toshi_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'] for address in addresses: if not validate_address(address): raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'bad_arguments', 'message': 'Bad Arguments' }] }) async with self.db: # see if this toshi_id is already registered, listening to it's own toshi_id rows = await self.db.fetch( "SELECT * FROM notification_registrations " "WHERE toshi_id = $1 AND eth_address = $1 AND service != 'ws'", toshi_id) if rows: if len(rows) > 1: log.warning( "LEGACY REGISTRATION FOR '{}' HAS MORE THAN ONE DEVICE OR SERVICE" .format(toshi_id)) registration_id = rows[0]['registration_id'] service = rows[0]['service'] else: service = 'LEGACY' registration_id = 'LEGACY' # simply store all the entered addresses with no service/registrations id for address in addresses: await self.db.execute( "INSERT INTO notification_registrations (toshi_id, service, registration_id, eth_address) " "VALUES ($1, $2, $3, $4) ON CONFLICT (toshi_id, service, registration_id, eth_address) DO NOTHING", toshi_id, service, registration_id, address) await self.db.commit() self.set_status(204)
async def post(self, service): toshi_id = self.verify_request() payload = self.json if 'registration_id' not in payload: raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'bad_arguments', 'message': 'Bad Arguments' }] }) # eth address verification (if none is supplied, delete all the matching addresses) if 'address' in payload: eth_addresses = [payload['address']] elif 'addresses' in payload: eth_addresses = payload['addresses'] if not type(eth_addresses) == list: raise JSONHTTPError(400, data={ 'id': 'bad_arguments', 'message': '`addresses` must be a list' }) else: eth_addresses = [] if not all( validate_address(eth_address) for eth_address in eth_addresses): raise JSONHTTPError(400, data={ 'id': 'bad_arguments', 'message': 'Bad Arguments' }) async with self.db: if eth_addresses: await self.db.executemany( "DELETE FROM notification_registrations WHERE toshi_id = $1 AND service = $2 AND registration_id = $3 and eth_address = $4", [(toshi_id, service, payload['registration_id'], eth_address) for eth_address in eth_addresses]) else: await self.db.execute( "DELETE FROM notification_registrations WHERE toshi_id = $1 AND service = $2 AND registration_id = $3", toshi_id, service, payload['registration_id']) await self.db.commit() request_to_migrate(toshi_id) self.set_status(204) self.track(toshi_id, "Deregistered ETH notifications")
async def get(self, key): if self.is_request_signed(): address = self.verify_request() await self.set_login_result(key, address) self.set_status(204) else: try: self._future = LoginManager.create_login_check(key) address = await self._future if address is None: raise JSONHTTPError(400, 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}) except TimeoutError: raise JSONHTTPError(408, body={ 'errors': [{ 'id': 'request_timeout', 'message': 'Login request timed out' }] }) except asyncio.CancelledError: raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'connection_closed', 'message': 'Login request connection closed' }] })
async def post(self, service): toshi_id = self.verify_request() payload = self.json if not all(arg in payload for arg in ['registration_id']): raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'bad_arguments', 'message': 'Bad Arguments' }] }) # eth address verification (default to toshi_id if eth_address is not supplied) if 'address' in payload: eth_addresses = [payload['address']] elif 'addresses' in payload: eth_addresses = payload['addresses'] if not type(eth_addresses) == list: raise JSONHTTPError(400, data={ 'id': 'bad_arguments', 'message': '`addresses` must be a list' }) else: raise JSONHTTPError(400, data={ 'id': 'bad_arguments', 'message': 'Bad Arguments' }) if not all( validate_address(eth_address) for eth_address in eth_addresses): raise JSONHTTPError(400, data={ 'id': 'bad_arguments', 'message': 'Bad Arguments' }) async with self.db: await self.db.executemany( "INSERT INTO notification_registrations (toshi_id, service, registration_id, eth_address) " "VALUES ($1, $2, $3, $4) ON CONFLICT (toshi_id, service, registration_id, eth_address) DO NOTHING", [(toshi_id, service, payload['registration_id'], eth_address) for eth_address in eth_addresses]) await self.db.commit() request_to_migrate(toshi_id) self.set_status(204)
async def get(self, tx_hash): self.set_header("Access-Control-Allow-Origin", "*") self.set_header("Access-Control-Allow-Headers", "x-requested-with") self.set_header('Access-Control-Allow-Methods', 'GET') format = self.get_query_argument('format', 'rpc').lower() try: tx = await ToshiEthJsonRPC(None, self.application, self.request).get_transaction(tx_hash) except JsonRPCError as e: raise JSONHTTPError(400, body={'errors': [e.data]}) if tx is None and format != 'sofa': raise JSONHTTPError( 404, body={'error': [{ 'id': 'not_found', 'message': 'Not Found' }]}) if format == 'sofa': async with self.db: row = await self.db.fetchrow( "SELECT * FROM transactions where hash = $1 ORDER BY transaction_id DESC", tx_hash) if row is None: raise JSONHTTPError(404, body={ 'error': [{ 'id': 'not_found', 'message': 'Not Found' }] }) if tx is None: tx = transaction_to_json( database_transaction_to_rlp_transaction(row)) if row['status'] == 'error': tx['error'] = True payment = SofaPayment.from_transaction( tx, networkId=self.application.config['ethereum']['network_id']) message = payment.render() self.set_header('Content-Type', 'text/plain') self.write(message.encode('utf-8')) else: self.write(tx)
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.toshi_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)
def is_request_signed(self, raise_if_partial=True): """Returns true if the request contains the headers needed to be considered signed. Designed for use in situations where a signature may be optional. if `raise_if_partial` is true (default) this will raise a HTTPError if the request contains only some of the headers needed for the signature verification, otherwise False will be returned""" count_token_headers = sum( 1 if x in self.request.headers else 0 for x in [ TOKEN_ID_ADDRESS_HEADER, TOKEN_SIGNATURE_HEADER, TOKEN_TIMESTAMP_HEADER ]) count_toshi_headers = sum( 1 if x in self.request.headers else 0 for x in [ TOSHI_ID_ADDRESS_HEADER, TOSHI_SIGNATURE_HEADER, TOSHI_TIMESTAMP_HEADER ]) if count_token_headers == 3 or count_toshi_headers == 3: return True if count_token_headers == 0 or count_toshi_headers == 0: return False if raise_if_partial: raise JSONHTTPError( 400, body={ 'errors': [{ 'id': 'bad_arguments', 'message': 'Missing headers required for authentication' }] })
async def post(self): try: # normalize inputs 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') elif 'to_address' not in self.json: self.json['to_address'] = None # the following are to deal with different representations # of the same concept from different places if 'gasPrice' in self.json: self.json['gas_price'] = self.json.pop('gasPrice') if 'gasprice' in self.json: self.json['gas_price'] = self.json.pop('gasprice') if 'startgas' in self.json: self.json['gas'] = self.json.pop('startgas') if 'gasLimit' in self.json: self.json['gas'] = self.json.pop('gasLimit') if 'networkId' in self.json: self.json['network_id'] = self.json.pop('networkId') if 'chainId' in self.json: self.json['network_id'] = self.json.pop('chainId') if 'chain_id' in self.json: self.json['network_id'] = self.json.pop('chain_id') if 'tokenAddress' in self.json: self.json['token_address'] = self.json.pop('tokenAddress') result = await ToshiEthJsonRPC( None, self.application, self.request).create_transaction_skeleton(**self.json) except JsonRPCError as e: log.warning("/tx/skel failed: " + json_encode(e.data) + "\" -> arguments: " + json_encode(self.json) + "\"") raise JSONHTTPError(400, body={'errors': [e.data]}) except TypeError: log.warning("/tx/skel failed: bad arguments \"" + json_encode(self.json) + "\"") raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'bad_arguments', 'message': 'Bad Arguments' }] }) self.write(result)
async def get(self, address): try: result = await ToshiEthJsonRPC(None, self.application, self.request).get_balance(address) except JsonRPCError as e: raise JSONHTTPError(400, body={'errors': [e.data]}) self.write(result)
async def get(self, eth_address, token_address=None): self.set_header("Access-Control-Allow-Origin", "*") self.set_header("Access-Control-Allow-Headers", "x-requested-with") self.set_header('Access-Control-Allow-Methods', 'GET') try: result = await ToshiEthJsonRPC(None, self.application, self.request).get_token_balances( eth_address, token_address=token_address) except JsonRPCError as e: raise JSONHTTPError(400, body={'errors': [e.data]}) if token_address: if result is None: raise JSONHTTPError(404) self.write(result) else: self.write({"tokens": result})
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})
async def get(self, token): key = "{}{}".format(AUTH_TOKEN_REDIS_PREFIX, token) toshi_id = await self.redis.get(key, encoding='utf-8') if toshi_id is not None: await self.redis.delete(key) async with self.db: user = await self.db.fetchrow( "SELECT * FROM users WHERE toshi_id = $1", toshi_id) if user: self.write(user_row_for_json(user)) return raise JSONHTTPError(404)
async def get(self, address, contract_address=None): self.set_header("Access-Control-Allow-Origin", "*") self.set_header("Access-Control-Allow-Headers", "x-requested-with") self.set_header('Access-Control-Allow-Methods', 'GET') try: result = await ToshiEthJsonRPC(None, self.application, self.request).get_collectibles( address, contract_address) except JsonRPCError as e: raise JSONHTTPError(400, body={'errors': [e.data]}) if result is None: raise JSONHTTPError( 404, body={'errors': [{ 'id': 'not_found', 'message': 'Not Found' }]}) self.write(result)
async def delete(self, contract_address): eth_address = self.verify_request() try: await ToshiEthJsonRPC( eth_address, self.application, self.request).remove_token(contract_address=contract_address) 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.set_status(204)
async def post(self): eth_address = self.verify_request() try: result = await ToshiEthJsonRPC(eth_address, self.application, self.request).add_token(**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: log.exception("bad_arguments") raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'bad_arguments', 'message': 'Bad Arguments' }] }) self.write(result)
async def get(self, address): self.set_header("Access-Control-Allow-Origin", "*") self.set_header("Access-Control-Allow-Headers", "x-requested-with") self.set_header('Access-Control-Allow-Methods', 'GET') try: result = await ToshiEthJsonRPC(None, self.application, self.request).get_balance(address) except JsonRPCError as e: raise JSONHTTPError(400, body={'errors': [e.data]}) self.write(result)
async def post(self): toshi_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'] for address in addresses: if not validate_address(address): raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'bad_arguments', 'message': 'Bad Arguments' }] }) async with self.db: await self.db.execute( "DELETE FROM notification_registrations WHERE service != 'ws' AND toshi_id = $1 AND ({})" .format(' OR '.join('eth_address = ${}'.format(i + 2) for i, _ in enumerate(addresses))), toshi_id, *addresses) await self.db.commit() self.set_status(204) self.track(toshi_id, "Deregistered ETH notifications")
async def post(self, service): toshi_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 # eth address verification (if none is supplied, delete all the matching addresses) eth_address = payload.get('address', None) if eth_address and not validate_address(eth_address): raise JSONHTTPError(data={ 'id': 'bad_arguments', 'message': 'Bad Arguments' }) async with self.db: args = [toshi_id, service, payload['registration_id']] if eth_address: args.append(eth_address) await self.db.execute( "DELETE FROM notification_registrations WHERE toshi_id = $1 AND service = $2 AND registration_id = $3{}" .format("AND eth_address = $4" if eth_address else ""), *args) await self.db.commit() self.set_status(204) self.track(toshi_id, "Deregistered ETH notifications")
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 toshi_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})
async def list_users(self, *, toshi_ids=None, payment_addresses=None): sql = "SELECT u.* FROM users u JOIN (VALUES " values = [] if toshi_ids is not None: column = 'toshi_id' addresses = toshi_ids elif payment_addresses is not None: column = 'payment_address' addresses = payment_addresses else: raise Exception( "list_users called without toshi_ids or payment_addresses") for i, address in enumerate(addresses): if not validate_address(address): raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'bad_arguments', 'message': 'Bad Arguments' }] }) values.append("('{}', {}) ".format(address, i)) # in case this request is made with a lot of ids # make sure we yield to other processes rather # than taking up all resources on this if i > 0 and i % 100 == 0: await asyncio.sleep(0.001) sql += ", ".join(values) sql += ") AS v ({column}, ordering) ON u.{column} = v.{column} ".format( column=column) sql += "ORDER BY v.ordering" async with self.db: rows = await self.db.fetch(sql) self.write({ 'limit': len(rows), 'total': len(rows), 'offset': 0, 'query': '', 'results': [user_row_for_json(row) for row in rows] })
async def get(self): client_filter = self.get_client_type async with self.db: try: offset = int(self.get_argument('offset', 0)) except ValueError: raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'invalid_offset', 'message': 'Invalid type for offset' }] }) try: limit = min( int(self.get_argument('limit', DEFAULT_DAPP_SEARCH_LIMIT)), MAX_DAPP_SEARCH_LIMIT) except ValueError: raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'invalid_limit', 'message': 'Invalid type for limit' }] }) try: category = self.get_argument('category', None) if category: category = int(category) except ValueError: raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'invalid_category', 'message': 'Invalid type for category' }] }) query = self.get_argument('query', '') dapps, total = await get_apps_by_filter( self.db, category, query, limit, offset, client_filter=client_filter, filter_special=self.should_filter_special_dapps) all_categories = await self.db.fetch( "SELECT * FROM categories WHERE hidden_on != 'all'{} ORDER BY category_id ASC" .format(" AND hidden_on != '{}'".format(client_filter) if client_filter != UNKNOWN_CLIENT else "")) all_category_ids = [cat['category_id'] for cat in all_categories] used_categories = filter_categories_in_dapps( dapps, all_category_ids) categories = {} for cat in all_categories: category_id = cat['category_id'] if category_id in used_categories: categories[category_id] = cat['name'] self.write({ 'results': { 'dapps': dapps, 'categories': categories }, 'offset': offset, 'limit': limit, 'total': total, 'query': query, 'category': category })
async def post(self, service): toshi_id = self.verify_request() payload = self.json if not all(arg in payload for arg in ['registration_id']): raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'bad_arguments', 'message': 'Bad Arguments' }] }) # TODO: registration id verification # XXX: BACKWARDS COMPAT FOR OLD PN REGISTARTION # remove when no longer needed if 'address' not in payload: async with self.db: legacy = await self.db.fetch( "SELECT eth_address FROM notification_registrations " "WHERE toshi_id = $1 AND service = 'LEGACY' AND registration_id = 'LEGACY'", toshi_id) else: legacy = False if legacy: async with self.db: for row in legacy: eth_address = row['eth_address'] await self.db.execute( "INSERT INTO notification_registrations (toshi_id, service, registration_id, eth_address) " "VALUES ($1, $2, $3, $4) ON CONFLICT (toshi_id, service, registration_id, eth_address) DO NOTHING", toshi_id, service, payload['registration_id'], eth_address) await self.db.execute( "DELETE FROM notification_registrations " "WHERE toshi_id = $1 AND service = 'LEGACY' AND registration_id = 'LEGACY'", toshi_id) await self.db.commit() else: # eth address verification (default to toshi_id if eth_address is not supplied) eth_address = payload[ 'address'] if 'address' in payload else toshi_id if not validate_address(eth_address): raise JSONHTTPError(data={ 'id': 'bad_arguments', 'message': 'Bad Arguments' }) async with self.db: await self.db.execute( "INSERT INTO notification_registrations (toshi_id, service, registration_id, eth_address) " "VALUES ($1, $2, $3, $4) ON CONFLICT (toshi_id, service, registration_id, eth_address) DO NOTHING", toshi_id, service, payload['registration_id'], eth_address) # XXX: temporary fix for old ios versions sending their payment address as toshi_id # should be removed after enough time has passed that most people should be using the fixed version if eth_address != toshi_id: # remove any apn registrations where toshi_id == eth_address for this eth_address await self.db.execute( "DELETE FROM notification_registrations " "WHERE toshi_id = $1 AND eth_address = $1 AND service = 'apn'", eth_address) await self.db.commit() self.set_status(204)
async def get(self, address): self.set_header("Access-Control-Allow-Origin", "*") self.set_header("Access-Control-Allow-Headers", "x-requested-with") self.set_header('Access-Control-Allow-Methods', 'GET') offset = parse_int(self.get_argument('offset', '0')) limit = parse_int(self.get_argument('limit', '10')) status = set([s.lower() for s in self.get_arguments('status')]) direction = set([d.lower() for d in self.get_arguments('direction')]) order = self.get_argument('order', 'desc').upper() if not validate_address(address) or \ offset is None or \ limit is None or \ (status and not status.issubset(['confirmed', 'unconfirmed', 'queued', 'error'])) or \ (direction and not direction.issubset(['in', 'out'])) or \ (order not in ['DESC', 'ASC']): raise JSONHTTPError(400, body={ 'id': 'bad_arguments', 'message': 'Bad Arguments' }) query = "SELECT * FROM transactions WHERE " args = [address, offset, limit] if len(direction) == 0 or len(direction) == 2: query += "(from_address = $1 OR to_address = $1) " elif 'in' in direction: query += "to_address = $1 " elif 'out' in direction: query += "from_address = $1 " if len(status) == 0: query += "AND (status != $4 OR status IS NULL) " args.append('error') else: status_query = [] for s in status: if s == 'queued': status_query.extend([ "status = ${}".format(len(args) + 1), "status IS NULL" ]) else: status_query.append("status = ${}".format(len(args) + 1)) args.append(s) query += "AND (" + " OR ".join(status_query) + ") " query += "ORDER BY created {} OFFSET $2 LIMIT $3".format(order) async with self.db: rows = await self.db.fetch(query, *args) transactions = [] for row in rows: transactions.append({ "hash": row['hash'], "to": row['to_address'], "from": row['from_address'], "nonce": hex(row['nonce']), "value": row['value'], "gas": row['gas'], "gas_price": row['gas_price'], "created_data": row['created'].isoformat(), "confirmed_data": row['updated'].isoformat() if row['blocknumber'] else None, "status": row['status'] if row['status'] is not None else 'queued', "data": row['data'] }) resp = { "transactions": transactions, "offset": offset, "limit": limit, "order": order } if len(direction) == 1: resp['direction'] = direction.pop() if status: resp['status'] = "&".join(status) self.write(resp)
def get_json_argument(self, name, default=DEFAULT_JSON_ARGUMENT): if name not in self.json: if default is DEFAULT_JSON_ARGUMENT: raise JSONHTTPError(400, "missing_arguments") return default return self.json[name]
def verify_request(self): """Verifies that the signature and the payload match the expected address raising a JSONHTTPError (400) if something is wrong with the request""" if TOSHI_ID_ADDRESS_HEADER in self.request.headers: expected_address = self.request.headers[ TOSHI_ID_ADDRESS_HEADER] elif self.get_argument(TOSHI_ID_ADDRESS_QUERY_ARG, None): expected_address = self.get_argument( TOSHI_ID_ADDRESS_QUERY_ARG) elif TOKEN_ID_ADDRESS_HEADER in self.request.headers: expected_address = self.request.headers[ TOKEN_ID_ADDRESS_HEADER] elif self.get_argument(TOKEN_ID_ADDRESS_QUERY_ARG, None): expected_address = self.get_argument( TOKEN_ID_ADDRESS_QUERY_ARG) else: raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'bad_arguments', 'message': 'Missing Toshi-ID-Address' }] }) if TOSHI_SIGNATURE_HEADER in self.request.headers: signature = self.request.headers[TOSHI_SIGNATURE_HEADER] elif self.get_argument(TOSHI_SIGNATURE_QUERY_ARG, None): signature = self.get_argument(TOSHI_SIGNATURE_QUERY_ARG) elif TOKEN_SIGNATURE_HEADER in self.request.headers: signature = self.request.headers[TOKEN_SIGNATURE_HEADER] elif self.get_argument(TOKEN_SIGNATURE_QUERY_ARG, None): signature = self.get_argument(TOKEN_SIGNATURE_QUERY_ARG) else: raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'bad_arguments', 'message': 'Missing Toshi-Signature' }] }) if TOSHI_TIMESTAMP_HEADER in self.request.headers: timestamp = self.request.headers[TOSHI_TIMESTAMP_HEADER] elif self.get_argument(TOSHI_TIMESTAMP_QUERY_ARG, None): timestamp = self.get_argument(TOSHI_TIMESTAMP_QUERY_ARG) elif TOKEN_TIMESTAMP_HEADER in self.request.headers: timestamp = self.request.headers[TOKEN_TIMESTAMP_HEADER] elif self.get_argument(TOKEN_TIMESTAMP_QUERY_ARG, None): timestamp = self.get_argument(TOKEN_TIMESTAMP_QUERY_ARG) else: raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'bad_arguments', 'message': 'Missing Toshi-Timestamp' }] }) timestamp = parse_int(timestamp) if timestamp is None: raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'invalid_timestamp', 'message': 'Given Toshi-Timestamp is invalid' }] }) if not validate_address(expected_address): raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'invalid_id_address', 'message': 'Invalid Toshi-ID-Address' }] }) if not validate_signature(signature): raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'invalid_signature', 'message': 'Invalid Toshi-Signature' }] }) try: signature = data_decoder(signature) except Exception: raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'invalid_signature', 'message': 'Invalid Toshi-Signature' }] }) verb = self.request.method uri = self.request.path if self.request.body: datahash = self.request.body else: datahash = "" data_string = generate_request_signature_data_string( verb, uri, timestamp, datahash) if not ecrecover(data_string, signature, expected_address): raise JSONHTTPError(400, body={ 'errors': [{ 'id': 'invalid_signature', 'message': 'Invalid Toshi-Signature' }] }) if abs(int(time.time()) - timestamp) > TIMESTAMP_EXPIRY: raise JSONHTTPError( 400, body={ 'errors': [{ 'id': 'invalid_timestamp', 'message': 'The difference between the timestamp and the current time is too large' }] }) return expected_address