def receipt_v1() -> Tuple[str, int, Dict[str, str]]: if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") content = flask.request.json try: dragonnet.process_receipt_v1(content) except NotImplementedError as e: raise exceptions.BadRequest(str(e)) return helpers.flask_http_response(200, helpers.format_success(True))
def query_transactions_v1(params: Dict[str, Any], parse: bool = True) -> "RSearch": """invoke queries on redisearch indexes Args: params: Dictionary of redisearch query options parse: If true, parse the transaction payload before returning Returns: {"results": [], "total": total} storage objects matching search query """ if not params.get("transaction_type"): raise exceptions.ValidationException( "transaction_type must be supplied for transaction queries") try: query_result = redisearch.search( index=params["transaction_type"], query_str=params["q"], only_id=params.get("id_only"), verbatim=params.get("verbatim"), offset=params.get("offset"), limit=params.get("limit"), sort_by=params.get("sort_by"), sort_asc=params.get("sort_asc"), ) except redis.exceptions.ResponseError as e: error_str = str(e) # Detect if this is a syntax error; if so, throw it back as a 400 with the message if error_str.startswith("Syntax error"): raise exceptions.BadRequest(error_str) # If unknown index, user provided a bad transaction type elif error_str == "Unknown Index name": raise exceptions.BadRequest("Invalid transaction type") else: raise result: "RSearch" = {"total": query_result.total, "results": []} if params.get("id_only"): result["results"] = [x.id for x in query_result.docs] else: transactions = [] for doc in query_result.docs: block_id = doc.block_id transaction_id = doc.id retrieved_txn = storage.select_transaction(block_id, transaction_id) if parse: retrieved_txn["payload"] = json.loads(retrieved_txn["payload"]) transactions.append(retrieved_txn) result["results"] = transactions return result
def query_blocks_v1(params: Dict[str, Any], parse: bool = False) -> "RSearch": """Returns block matching block id, with query parameters accepted. Args: block_id: string Block id to search for. params: Dictionary of redisearch query options parse: whether or not we should parse contents """ try: query_result = redisearch.search( index=redisearch.Indexes.block.value, query_str=params["q"], only_id=params.get("id_only"), offset=params.get("offset"), limit=params.get("limit"), sort_by=params.get("sort_by"), sort_asc=params.get("sort_asc"), ) except redis.exceptions.ResponseError as e: # Detect if this is a syntax error; if so, throw it back as a 400 with the message if str(e).startswith("Syntax error"): raise exceptions.BadRequest(str(e)) else: raise result: "RSearch" = {"total": query_result.total, "results": []} if params.get("id_only"): result["results"] = [x.id for x in query_result.docs] else: blocks = [] for block in query_result.docs: blocks.append(get_block_by_id_v1(block.id, parse)) result["results"] = blocks return result
def sign_transaction(self, raw_transaction: Dict[str, Any]) -> str: """Sign a transaction for this network Args: raw_transaction: The dictionary of the raw transaction containing: to: hex string of the to address value: The amount of eth (in wei) to send (in hex string) data: Optional hex string for arbitrary data nonce: Optional field for nonce (will automatically determine if not provided) gasPrice: Optional field to set gasPrice of the transaction (as a hex string in wei) gas: Optional gas limit for this transaction (as a hex string). Defaults to 60000 Returns: String of the signed transaction as hex """ raw_transaction["from"] = self.address raw_transaction["chainId"] = self.chain_id if not raw_transaction.get("nonce"): raw_transaction["nonce"] = self.w3.toHex( self.w3.eth.getTransactionCount(self.address)) if not raw_transaction.get("gasPrice"): raw_transaction["gasPrice"] = self.w3.toHex( self._calculate_transaction_fee()) if not raw_transaction.get("gas"): # TODO proper gas limit estimation raw_transaction["gas"] = self.w3.toHex(STANDARD_GAS_LIMIT) try: _log.info(f"[ETHEREUM] Signing raw transaction: {raw_transaction}") return self.w3.eth.account.sign_transaction( raw_transaction, self.priv_key).rawTransaction.hex() except Exception as e: raise exceptions.BadRequest(f"Error signing transaction: {e}")
def get_transaction_v1(transaction_id: str) -> Tuple[str, int, Dict[str, str]]: if not transaction_id: raise exceptions.BadRequest("Paramterer 'transaction_id' is required'") should_parse = flask.request.headers.get("Parse-Payload") != "false" return helpers.flask_http_response( 200, transactions.get_transaction_v1(transaction_id, should_parse))
def _build_transaction_msg(self, raw_transaction: Dict[str, Any]) -> Dict: """Build a formatted transaction for this network from the base parameters Args: amount (int): amount of token in transaction to_address (str): hex of the address to send to symbol (str, optional): the exchange symbol for the token (defaults to 'BNB') memo (str, optional): string of data to publish in the transaction (defaults to '') Returns: dict of the constructed transaction """ amount = raw_transaction["amount"] if amount <= 0: raise exceptions.BadRequest("[BINANCE] Amount in transaction cannot be less than or equal to 0.") symbol = raw_transaction.get("symbol") or "BNB" memo = raw_transaction.get("memo") or "" inputs = {"address": self.address, "coins": [{"amount": amount, "denom": symbol}]} outputs = {"address": raw_transaction["to_address"], "coins": [{"amount": amount, "denom": symbol}]} response = self._fetch_account() transaction_data = { "account_number": response["account_number"], "sequence": response["sequence"], "from": self.address, "msgs": [{"type": "cosmos-sdk/Send", "inputs": [inputs], "outputs": [outputs]}], "memo": memo, } return transaction_data
def public_blockchain_transaction_v1() -> Tuple[str, int, Dict[str, str]]: if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") data = flask.request.json if not data.get("network"): raise exceptions.ValidationException("Invalid parameter: network") try: if data["network"] in ["BTC_MAINNET", "BTC_TESTNET3"]: _validate_bitcoin_transaction_v1(data.get("transaction")) elif data["network"] in [ "ETH_MAINNET", "ETH_ROPSTEN", "ETC_MAINNET", "ETC_MORDEN" ]: _validate_ethereum_transaction_v1(data.get("transaction")) else: raise exceptions.ValidationException( "User input did not match JSON schema") except fastjsonschema.JsonSchemaException: raise exceptions.ValidationException( "User input did not match JSON schema") return helpers.flask_http_response( 200, interchain.legacy_sign_blockchain_transaction_v1( data["network"], data["transaction"]))
def sign_transaction(self, raw_transaction: Dict[str, Any]) -> str: """Sign a transaction for this network Args: amount (int): amount of token in transaction to_address (str): hex of the address to send to symbol (str, optional): the exchange symbol for the token (defaults to 'BNB') memo (str, optional): string of data to publish in the transaction (defaults to '') Returns: String of the signed transaction as base64 """ built_transaction = self._build_transaction_msg(raw_transaction) try: if self.testnet: tx = TestBnbTransaction.from_obj(built_transaction) else: # mainnet tx = BnbTransaction.from_obj(built_transaction) _log.info( f"[BINANCE] Signing raw transaction: {tx.signing_json()}") mykeys = keys.DCKeys(pull_keys=False) mykeys.initialize(private_key_string=self.b64_private_key) signature = base64.b64decode( mykeys.make_binance_signature(content=tx.signing_json())) tx.apply_sig(signature, self.pub.serialize(compressed=True)) signed_transaction_bytes = tx.encode() # signed_transaction expected to be base64, not hex: return base64.b64encode(signed_transaction_bytes).decode("ascii") except Exception as e: raise exceptions.BadRequest( f"[BINANCE] Error signing transaction: {e}")
def create_binance_interchain_v1(**kwargs) -> Tuple[str, int, Dict[str, str]]: if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") data = flask.request.json try: _validate_binance_network_create_v1(data) except fastjsonschema.JsonSchemaException as e: raise exceptions.ValidationException(str(e)) return helpers.flask_http_response( 201, interchain.create_binance_interchain_v1(data))
def get_smart_contract_logs_v1( contract_id: str) -> Tuple[str, int, Dict[str, str]]: since = flask.request.args.get("since") tail = cast(Any, flask.request.args.get("tail")) try: tail = int(tail) except Exception: raise exceptions.BadRequest("Invalid parameter for tail") return helpers.flask_http_response( 200, smart_contracts.get_logs_v1(contract_id, since, tail))
def update_bitcoin_interchain_v1(name: str) -> Tuple[str, int, Dict[str, str]]: if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") data = flask.request.json try: _validate_bitcoin_network_update_v1(data) except fastjsonschema.JsonSchemaException as e: raise exceptions.ValidationException(str(e)) return helpers.flask_http_response( 200, interchain.update_bitcoin_interchain_v1(name, data))
def set_default_network_v1(**kwargs) -> Tuple[str, int, Dict[str, str]]: if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") data = flask.request.json try: _validate_set_default_interchain_v1(data) except fastjsonschema.JsonSchemaException as e: raise exceptions.ValidationException(str(e)) return helpers.flask_http_response( 200, interchain.set_default_interchain_v1(data["blockchain"], data["name"]))
def update_contract_v1(contract_id: str) -> Tuple[str, int, Dict[str, str]]: if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") update = flask.request.json try: _validate_sc_update_v1(update) except fastjsonschema.JsonSchemaException: raise exceptions.ValidationException("User input did not match JSON schema") return helpers.flask_http_response(202, smart_contracts.update_contract_v1(contract_id, update))
def create_binance_transaction_v1(name: str, **kwargs) -> Tuple[str, int, Dict[str, str]]: if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") data = flask.request.json try: _validate_binance_transaction_v1(data) except fastjsonschema.JsonSchemaException as e: raise exceptions.ValidationException(str(e)) return helpers.flask_http_response( 200, interchain.sign_interchain_transaction_v1("binance", name, data))
def create_ethereum_interchain_v1() -> Tuple[str, int, Dict[str, str]]: if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") data = flask.request.json try: _validate_ethereum_network_create_v1(data) except fastjsonschema.JsonSchemaException: raise exceptions.ValidationException( "User input did not match JSON schema") return helpers.flask_http_response( 201, interchain.create_ethereum_interchain_v1(data))
def create_bitcoin_transaction_v1( name: str) -> Tuple[str, int, Dict[str, str]]: if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") data = flask.request.json try: _validate_bitcoin_transaction_v1(data) except fastjsonschema.JsonSchemaException: raise exceptions.ValidationException( "User input did not match JSON schema") return helpers.flask_http_response( 200, interchain.sign_interchain_transaction_v1("bitcoin", name, data))
def register_transaction_type_v1() -> Tuple[str, int, Dict[str, str]]: if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") content = flask.request.json try: _validate_create_txn_type_v1(content) except fastjsonschema.JsonSchemaException: raise exceptions.ValidationException("User input did not match JSON schema") transaction_types.register_transaction_type_v1(content) return helpers.flask_http_response(200, helpers.format_success(True))
def post_contract_v1() -> Tuple[str, int, Dict[str, str]]: if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") contract = flask.request.json try: _validate_sc_create_v1(contract) if contract.get("custom_indexes"): helpers.verify_custom_indexes_options(contract.get("custom_indexes")) except fastjsonschema.JsonSchemaException: raise exceptions.ValidationException("User input did not match JSON schema") return helpers.flask_http_response(202, smart_contracts.create_contract_v1(contract))
def post_transaction_v1(**kwargs) -> Tuple[str, int, Dict[str, str]]: if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") txn = flask.request.json try: _validate_txn_create_v1(txn) except fastjsonschema.JsonSchemaException as e: raise exceptions.ValidationException(str(e)) return helpers.flask_http_response( 201, transactions.submit_transaction_v1(txn, flask.request.headers.get("X-Callback-URL"), api_key=kwargs["used_auth_key"]) )
def enqueue_v1() -> Tuple[str, int, Dict[str, str]]: if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") content = flask.request.json deadline = int(cast(str, flask.request.headers.get( "deadline"))) if flask.request.headers.get("deadline") else 30 try: _validate_broadcast_schema_v1(content) except fastjsonschema.JsonSchemaException as e: raise exceptions.ValidationException(str(e)) dragonnet.enqueue_item_for_verification_v1(content, deadline) return helpers.flask_http_response(200, helpers.format_success(True))
def register_transaction_type_v1(**kwargs) -> Tuple[str, int, Dict[str, str]]: if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") content = flask.request.json try: _validate_create_txn_type_v1(content) if content.get("custom_indexes"): helpers.verify_custom_indexes_options(content["custom_indexes"]) except fastjsonschema.JsonSchemaException as e: raise exceptions.ValidationException(str(e)) transaction_types.register_transaction_type_v1(content) return helpers.flask_http_response(200, helpers.format_success(True))
def dragonnet_auth_v1() -> Tuple[str, int, Dict[str, str]]: """ Create a new DragonNet interchain communication key pair with the requester (unauthenticated by design) """ if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") body = flask.request.json try: _validate_interchain_auth_v1(body) except fastjsonschema.JsonSchemaException as e: raise exceptions.ValidationException(str(e)) dragonnet.register_interchain_auth_v1(body) return helpers.flask_http_response(201, helpers.format_success(True))
def update_api_key_v1(key_id: str) -> Tuple[str, int, Dict[str, str]]: if not key_id: raise exceptions.ValidationException("Invalid parameter: key_id") if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") body = flask.request.json try: _validate_api_key_update_v1(body) except fastjsonschema.JsonSchemaException as e: raise exceptions.ValidationException(str(e)) api_keys.update_api_key_v1(key_id, body["nickname"]) return helpers.flask_http_response(200, helpers.format_success(True))
def update_transaction_type_v1(txn_type: str) -> Tuple[str, int, Dict[str, str]]: if not txn_type: raise exceptions.ValidationException("Invalid parameter: txn_type") if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") content = flask.request.json try: _validate_update_txn_type_v1(content) except fastjsonschema.JsonSchemaException: raise exceptions.ValidationException("User input did not match JSON schema") transaction_types.update_transaction_type_v1(txn_type, content["custom_indexes"]) return helpers.flask_http_response(200, helpers.format_success(True))
def post_transaction_v1() -> Tuple[str, int, Dict[str, str]]: if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") txn = flask.request.json try: _validate_txn_create_v1(txn) except fastjsonschema.JsonSchemaException: raise exceptions.ValidationException( "User input did not match JSON schema") return helpers.flask_http_response( 201, transactions.submit_transaction_v1( txn, flask.request.headers.get("X-Callback-URL")))
def get_sc_heap_v1(key: str) -> Tuple[str, int]: """ /v1/get/<contract_id>/HEAP/<key> method = GET path = '/' seperate string where the left side is the contract_id and the right side is the object key in the heap Get a value from the smart contract heap in storage """ initial_index = key.find("/") if initial_index == -1: raise exceptions.BadRequest("Path must look like /v1/get/<contract_id>/<object_key>") contract_id = key[:initial_index] path = key[initial_index:] return ( smart_contracts.heap_get_v1(contract_id, path), 200, ) # Explicitly not using helpers.flask_http_response, because response isn't necessarily JSON
def _fetch_account(self): """Fetch the account metadata for an address Returns: response containing account metadata """ _log.info(f"[BINANCE] Fetching address metadata for {self.address}") path = f"account/{self.address}" # params expected inside path string try: response = self._call_node_api(path) if response.status_code == 404: _log.warning("[BINANCE] 404 response from Binance node:") _log.warning("[BINANCE] Address not found -- likely has zero funds.") raise exceptions.BadRequest("[BINANCE] Error fetching metadata from 'account' endpoint.") return response.json() except exceptions.InterchainConnectionError: _log.warning("[BINANCE] Non 200 response from Binance node.") _log.warning("[BINANCE] May have been a 500 Bad Request or a 404 Not Found.") raise exceptions.InterchainConnectionError("[BINANCE] Error fetching metadata from 'account' endpoint.")
def post_transaction_bulk_v1(**kwargs) -> Tuple[str, int, Dict[str, str]]: """ Enqueue bulk transactions to be processed """ if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") content = flask.request.json try: _validate_bulk_txn_create_v1(content) except fastjsonschema.JsonSchemaException as e: raise exceptions.ValidationException(str(e)) response = transactions.submit_bulk_transaction_v1(content, api_key=kwargs["used_auth_key"]) if not response["201"]: return helpers.flask_http_response(400, response) return helpers.flask_http_response(207, response)
def post_transaction_bulk_v1() -> Tuple[str, int, Dict[str, str]]: """ Enqueue bulk transactions to be processed """ if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") content = flask.request.json try: _validate_bulk_txn_create_v1(content) except fastjsonschema.JsonSchemaException: raise exceptions.ValidationException( "User input did not match JSON schema") response = transactions.submit_bulk_transaction_v1(content) if not response["201"]: return helpers.flask_http_response(400, response) return helpers.flask_http_response(207, response)
def update_api_key_v1(key_id: str, **kwargs) -> Tuple[str, int, Dict[str, str]]: if not key_id: raise exceptions.ValidationException("Invalid parameter: key_id") if not flask.request.is_json: raise exceptions.BadRequest("Could not parse JSON") body = flask.request.json try: _validate_api_key_update_v1(body) except fastjsonschema.JsonSchemaException as e: raise exceptions.ValidationException(str(e)) return helpers.flask_http_response( 200, api_keys.update_api_key_v1(key_id, body.get("nickname"), body.get("permissions_document")))