import grp from flask import Flask, request, jsonify from json import JSONEncoder import json import grpc # Here are Iroha dependencies. # Python library generally consists of 3 parts: # Iroha, IrohaCrypto and IrohaGrpc which we need to import: import os import binascii from iroha import IrohaCrypto from iroha import Iroha, IrohaGrpc from werkzeug.wrappers import response from werkzeug.exceptions import HTTPException # Here is the information about the environment and admin account information: IROHA_HOST_ADDR = os.getenv('IROHA_HOST_ADDR', '127.0.0.1') IROHA_PORT = os.getenv('IROHA_PORT', '50051') ADMIN_ACCOUNT_ID = os.getenv('ADMIN_ACCOUNT_ID', 'admin@test') ADMIN_PRIVATE_KEY = os.getenv( 'ADMIN_PRIVATE_KEY', 'f101537e319568c765b2cc89698325604991dca57b9716b58016b253506cab70') # Here we will create user keys user_private_key = IrohaCrypto.private_key() user_public_key = IrohaCrypto.derive_public_key(user_private_key) iroha = Iroha(ADMIN_ACCOUNT_ID) net = IrohaGrpc('{}:{}'.format(IROHA_HOST_ADDR, IROHA_PORT)) class MyEncoder(JSONEncoder): def default(self, o): if isinstance(o, bytes): return str(o, encoding='utf-8') return o.__dict__ app = Flask(__name__) app.json_encoder = MyEncoder class Asset: assetId = "" balance = 0 def __init__(self, assetId = "", balance = 0): self.assetId = assetId self.balance = balance class Assets: assets = [] class StatefulValidationError: name = "" description = "" solution = "" def __init__(self, name = "", description = "", solution = ""): self.name = name self.description = description self.solution = solution transferAssetsValidationErrors = [ StatefulValidationError("Could not transfer asset", "Internal error happened", "Try again or contact developers"), StatefulValidationError("No such permissions", "Command's creator does not have permission to transfer asset from his account", "Grant the necessary permission"), StatefulValidationError("No such source account", "Cannot find account with such id to transfer money from", "Make sure source account id is correct"), StatefulValidationError("No such destination account", "Cannot find account with such id to transfer money to", "Make sure destination account id is correct"), StatefulValidationError("No such asset found", "Cannot find such asset", "Make sure asset name and precision are correct"), StatefulValidationError("Not enough balance", "Source account's balance is too low to perform the operation", "Add asset to account or choose lower value to subtract"), StatefulValidationError("Too much asset to transfer", "Resulting asset quantity of destination account would exceed the allowed maximum", "Make sure that the final destination value is less than 2^256 / 10^asset_precision"), StatefulValidationError("Too long description", "Too long description", "Ensure that description length matches the criteria above (or just shorten it)") ] @app.errorhandler(Exception) def handle_exception(e): # pass through HTTP errors if isinstance(e, HTTPException): return e if isinstance(e, grpc.RpcError): return { "details" : e.details(), "debug_error" : json.loads(e.debug_error_string()) }, 500 # now you're handling non-HTTP exceptions only return e, 500 @app.route("/", methods=['GET']) def Hello(): return "IROHA REST API" @app.route("/accounts//assets/", methods=['GET']) def get_account_assets(accountId): #TODO: add validation and error handling query = iroha.query('GetAccountAssets', account_id=accountId) IrohaCrypto.sign_query(query, ADMIN_PRIVATE_KEY) iroha_response = net.send_query(query) data = iroha_response.account_assets_response.account_assets response = Assets() response.assets = [] if len(data) < 1: return '', 204 for asset in data: response.assets.append(Asset(asset.asset_id, asset.balance)) return jsonify(response) @app.route("/assets/transfer/", methods=['POST']) def transfer_assets(): data = request.get_json() if data["transfers"] and len(data["transfers"]) > 0: commands = [] for transfer in data["transfers"]: commands.append( iroha.command('TransferAsset', src_account_id = transfer["source"], dest_account_id = transfer["destination"], asset_id = transfer["asset"], description = transfer["description"], amount = str(transfer["amount"])) ) transaction = iroha.transaction(commands) hex_hash = binascii.hexlify(IrohaCrypto.hash(transaction)) IrohaCrypto.sign_transaction(transaction, ADMIN_PRIVATE_KEY) net.send_tx(transaction) #TODO speed up status reading transactionStates = [] for status in net.tx_status_stream(transaction): transactionStates.append(status) if(status[0] == "STATEFUL_VALIDATION_SUCCESS"): break if(status[0] == "STATEFUL_VALIDATION_FAILED"): return jsonify({ "transactionHash" : hex_hash, "error" : transferAssetsValidationErrors[status[2] - 1] }), 400 #TODO send URL to newly created transaction in Content-Location header return jsonify({ "transactionHash" : hex_hash, # "transactionStates" : transactionStates })