class RFAIEventConsumer(EventConsumer):
    def __init__(self, net_id, ws_provider, ipfs_url, ipfs_port):
        self._ipfs_util = IPFSUtil(ipfs_url, ipfs_port)
        self._blockchain_util = BlockChainUtil("WS_PROVIDER", ws_provider)
        self._net_id = net_id

    def on_event(self, event):
        pass

    def _get_rfai_contract(self):
        base_contract_path = os.path.abspath(
            os.path.join(os.path.dirname(__file__), '..', '..', 'node_modules',
                         'singularitynet-rfai-contracts'))
        rfai_contract = self._blockchain_util.get_contract_instance(
            base_contract_path, "RFAI", self._net_id)

        return rfai_contract

    def _get_rfai_metadata_from_ipfs(self, ipfs_hash):
        return self._ipfs_util.read_file_from_ipfs(ipfs_hash)

    def _get_event_data(self, event):
        return eval(event['data']['json_str'])

    def _get_metadata_hash(self, metadata_uri):
        return metadata_uri.decode("utf-8")

    def _get_rfai_service_request_by_id(self, request_id):
        rfai_contract = self._get_rfai_contract()
        result = self._blockchain_util.call_contract_function(
            rfai_contract, "getServiceRequestById", request_id)
        return result
Ejemplo n.º 2
0
 def __init__(
     self,
     ws_provider,
     repository=None,
 ):
     self._blockchain_util = BlockChainUtil("WS_PROVIDER", ws_provider)
     self._event_repository = EventRepository(repository)
 def __init__(self, repo, net_id):
     self.net_id = net_id
     self.repo = repo
     self.obj_blockchain_util = BlockChainUtil(
         provider_type="HTTP_PROVIDER",
         provider=NETWORKS[NETWORK_ID]['http_provider'])
     self.obj_channel_transaction_history_dao = ChannelTransactionStatusDataAccessObject(
         repo=self.repo)
 def __init__(self, repo):
     self.repo = repo
     self.boto_utils = BotoUtils(region_name=REGION_NAME)
     self.blockchain_util = BlockChainUtil(
         provider_type="HTTP_PROVIDER",
         provider=NETWORKS[NETWORK_ID]['http_provider'])
     self.utils = Utils()
     self.channel_dao = ChannelDAO(repo=self.repo)
     self.wallet_dao = WalletDAO(repo=self.repo)
    def open_channel_by_third_party(self, order_id, sender, sender_private_key,
                                    group_id, amount, currency, recipient):
        obj_wallet_dao = WalletDAO(obj_repo=self.obj_repo)
        self.obj_blockchain_util = BlockChainUtil(
            provider_type="HTTP_PROVIDER",
            provider=NETWORKS[NETWORK_ID]['http_provider'])
        method_name = "openChannelByThirdParty"
        self.mpe_address = self.obj_blockchain_util.read_contract_address(
            net_id=NETWORK_ID, path=MPE_ADDR_PATH, key='address')
        current_block_no = self.obj_blockchain_util.get_current_block_no()
        # 1 block no is mined in 15 sec on average, setting expiration as 10 years
        expiration = current_block_no + (10 * 365 * 24 * 60 * 4)
        agi_tokens = self.__calculate_agi_tokens(amount=amount,
                                                 currency=currency)
        group_id_in_hex = "0x" + base64.b64decode(group_id).hex()
        r, s, v, signature = self.__generate_signature_details(
            recipient=recipient,
            group_id=group_id_in_hex,
            agi_tokens=agi_tokens,
            expiration=expiration,
            message_nonce=current_block_no,
            signer_key=sender_private_key)
        positional_inputs = (sender, SIGNER_ADDRESS, recipient,
                             group_id_in_hex, agi_tokens, expiration,
                             current_block_no, v, r, s)
        transaction_object = self.obj_blockchain_util.create_transaction_object(
            *positional_inputs,
            method_name=method_name,
            address=EXECUTOR_WALLET_ADDRESS,
            contract_path=MPE_CNTRCT_PATH,
            contract_address_path=MPE_ADDR_PATH,
            net_id=NETWORK_ID)
        raw_transaction = self.obj_blockchain_util.sign_transaction_with_private_key(
            transaction_object=transaction_object,
            private_key=EXECUTOR_WALLET_KEY)
        transaction_hash = self.obj_blockchain_util.process_raw_transaction(
            raw_transaction=raw_transaction)
        print("openChannelByThirdParty::transaction_hash", transaction_hash)
        obj_wallet_dao.insert_channel_history(
            order_id=order_id,
            amount=amount,
            currency=currency,
            type=method_name,
            address=sender,
            signature=signature,
            request_parameters=str(positional_inputs),
            transaction_hash=transaction_hash,
            status=0)

        return {
            "transaction_hash": transaction_hash,
            "signature": signature,
            "agi_tokens": agi_tokens,
            "positional_inputs": positional_inputs,
            "type": method_name
        }
 def __init__(self, obj_repo):
     self.repo = obj_repo
     self.obj_transaction_history_dao = TransactionHistoryDAO(self.repo)
     self.lambda_client = boto3.client('lambda', region_name=REGION_NAME)
     self.boto_client = BotoUtils(REGION_NAME)
     self.wallet_service = WalletService()
     self.obj_blockchain_util = BlockChainUtil(
         provider_type="HTTP_PROVIDER",
         provider=NETWORKS[NETWORK_ID]['http_provider']
     )
     self.utils = Utils()
 def verify_current_block_number(self):
     signed_block_number = int(
         self.event['headers']['x-currentblocknumber'])
     blockchain_util = BlockChainUtil(provider_type="WS_PROVIDER", provider=self.networks[self.net_id]['ws_provider'])
     current_block_number = blockchain_util.get_current_block_no()
     print(f"current block {current_block_number}\n"
           f"signed clock number {signed_block_number}")
     if current_block_number > signed_block_number + self.BLOCK_LIMIT or current_block_number < signed_block_number - self.BLOCK_LIMIT:
         print("current_block_number is more than signed block limit %s",
               current_block_number)
         return False
     return True
Ejemplo n.º 8
0
 def __init__(self, net_id):
     self.net_id = net_id
     self.lambda_client = boto3.client("lambda", region_name=REGION_NAME)
     self.obj_utils = Utils()
     self.obj_blockchain_utils = BlockChainUtil(
         provider_type="HTTP_PROVIDER",
         provider=NETWORKS[self.net_id]["http_provider"],
     )
     self.mpe_address = self.obj_blockchain_utils.read_contract_address(
         net_id=self.net_id, path=MPE_ADDR_PATH, key="address")
     self.current_block_no = self.obj_blockchain_utils.get_current_block_no(
     )
Ejemplo n.º 9
0
 def __init__(self, env_type):
     self.__env_type = env_type
     if env_type == EnvironmentType.MAIN.value:
         self.__network_id = NETWORK_ID
         self.__contract_path = REG_CNTRCT_PATH
         self.__executor_address = ""
         self.__contract_address_path = REG_ADDR_PATH
         self.__blockchain_util = BlockChainUtil(
             provider_type="HTTP_PROVIDER",
             provider=NETWORKS[self.__network_id]['http_provider'])
     else:
         raise MethodNotImplemented()
 def create_and_register_wallet(self):
     self.obj_blockchain_util = BlockChainUtil(
         provider_type="HTTP_PROVIDER",
         provider=NETWORKS[NETWORK_ID]['http_provider'])
     address, private_key = self.obj_blockchain_util.create_account()
     obj_wallet = Wallet(address=address,
                         private_key=private_key,
                         type=GENERAL_WALLET_TYPE,
                         status=0)
     registered = self.register_wallet(obj_wallet=obj_wallet)
     if registered:
         return obj_wallet.get_wallet()
     raise Exception("Unable to create and register wallet.")
Ejemplo n.º 11
0
class BlockchainEventProducer(EventProducer):
    def __init__(
        self,
        ws_provider,
        repository=None,
    ):
        self._blockchain_util = BlockChainUtil("WS_PROVIDER", ws_provider)
        self._event_repository = EventRepository(repository)

    def _get_base_contract_path(self):
        pass

    def _get_events_from_blockchain(self, start_block_number, end_block_number,
                                    net_id):

        base_contract_path = self._get_base_contract_path()
        contract = self._blockchain_util.get_contract_instance(
            base_contract_path, self._contract_name, net_id=net_id)
        contract_events = contract.events
        all_blockchain_events = []

        for attributes in contract_events.abi:
            if attributes['type'] == 'event':
                event_name = attributes['name']
                event_object = getattr(contract.events, event_name)
                blockchain_events = event_object.createFilter(
                    fromBlock=start_block_number,
                    toBlock=end_block_number).get_all_entries()
                all_blockchain_events.extend(blockchain_events)

        return all_blockchain_events

    def _produce_contract_events(self, start_block_number, end_block_number,
                                 net_id):

        events = self._get_events_from_blockchain(start_block_number,
                                                  end_block_number, net_id)
        logger.info(f"read no of events {len(events)}")
        return events

    def _get_end_block_number(self, last_processed_block_number, batch_limit):
        current_block_number = self._blockchain_util.get_current_block_no()
        end_block_number = last_processed_block_number + batch_limit
        if current_block_number <= end_block_number:
            end_block_number = current_block_number

        return end_block_number

    def produce_event(self, net_id):
        pass
 def setUp(self):
     self.net_id = 3
     self.http_provider = Web3.HTTPProvider(
         NETWORKS[self.net_id]['http_provider'])
     self.obj_utils = BlockChainUtil(provider_type="HTTP_PROVIDER",
                                     provider=self.http_provider)
     self.mpe_address = self.obj_utils.read_contract_address(
         net_id=self.net_id, path=MPE_ADDR_PATH, key='address')
     self.recipient = "0x9c302750c50307D3Ad88eaA9a6506874a15cE4Cb"
     self.group_id = "0x" + base64.b64decode(
         "DS2OoKSfGE/7hAO/023psl4Qdj0MJvYsreJbCoAHVSc=").hex()
     self.agi_tokens = 1
     self.current_block_no = self.obj_utils.get_current_block_no()
     self.expiration = self.current_block_no + 10000000
     self.channel_id = 0
class MPEEventConsumer(EventConsumer):
    _mpe_repository = MPERepository(Repository(NETWORK_ID, NETWORKS=NETWORKS))

    def __init__(self, ws_provider):
        self.blockchain_util = BlockChainUtil("WS_PROVIDER", ws_provider)

    def on_event(self, event):
        net_id = NETWORK_ID
        base_contract_path = os.path.abspath(
            os.path.join(os.path.dirname(__file__), '..', '..', 'node_modules',
                         'singularitynet-platform-contracts'))
        mpe_contract = self.blockchain_util.get_contract_instance(
            base_contract_path, "MPE", net_id)

        logger.info(f"processing mpe event {event}")
        event_name = event["name"]
        event_data = event["data"]
        mpe_data = eval(event_data['json_str'])
        channel_id = int(mpe_data['channelId'])

        if event_name == 'ChannelOpen':
            self._mpe_repository.create_channel(mpe_data)
        else:

            channel_data = mpe_contract.functions.channels(channel_id).call()
            group_id = base64.b64encode(channel_data[4]).decode('utf8')
            self._mpe_repository.update_channel(channel_id=channel_id,
                                                group_id=group_id,
                                                channel_data=channel_data)
Ejemplo n.º 14
0
class ServiceEventConsumer(EventConsumer):
    _connection = Repository(NETWORK_ID, NETWORKS=NETWORKS)
    _service_repository = ServiceRepository(_connection)

    def __init__(self, ws_provider, ipfs_url, ipfs_port):
        self._blockchain_util = BlockChainUtil("WS_PROVIDER", ws_provider)
        self._s3_util = S3Util(S3_BUCKET_ACCESS_KEY, S3_BUCKET_SECRET_KEY)
        self._ipfs_util = IPFSUtil(ipfs_url, ipfs_port)

    def on_event(self, event):
        pass

    def _fetch_tags(self, registry_contract, org_id_hex, service_id_hex):
        tags_data = registry_contract.functions.getServiceRegistrationById(
            org_id_hex, service_id_hex).call()
        return tags_data

    def _get_org_id_from_event(self, event):
        event_data = event['data']
        service_data = eval(event_data['json_str'])
        org_id_bytes = service_data['orgId']
        org_id = Web3.toText(org_id_bytes).rstrip("\x00")
        return org_id

    def _get_service_id_from_event(self, event):
        event_data = event['data']
        service_data = eval(event_data['json_str'])
        service_id_bytes = service_data['serviceId']
        service_id = Web3.toText(service_id_bytes).rstrip("\x00")
        return service_id

    def _get_metadata_uri_from_event(self, event):
        event_data = event['data']
        service_data = eval(event_data['json_str'])
        metadata_uri = Web3.toText(
            service_data['metadataURI'])[7:].rstrip("\u0000")
        return metadata_uri

    def _get_registry_contract(self):
        net_id = NETWORK_ID
        base_contract_path = os.path.abspath(
            os.path.join(os.path.dirname(__file__), '..', '..', 'node_modules',
                         'singularitynet-platform-contracts'))
        registry_contract = self._blockchain_util.get_contract_instance(
            base_contract_path, "REGISTRY", net_id)
        return registry_contract

    def _get_service_details_from_blockchain(self, event):
        logger.info(f"processing service event {event}")

        registry_contract = self._get_registry_contract()
        org_id = self._get_org_id_from_event(event)
        service_id = self._get_service_id_from_event(event)

        tags_data = self._fetch_tags(registry_contract=registry_contract,
                                     org_id_hex=org_id.encode("utf-8"),
                                     service_id_hex=service_id.encode("utf-8"))

        return org_id, service_id, tags_data
class ChannelTransactionStatusService:
    def __init__(self, repo, net_id):
        self.net_id = net_id
        self.repo = repo
        self.obj_blockchain_util = BlockChainUtil(
            provider_type="HTTP_PROVIDER",
            provider=NETWORKS[NETWORK_ID]['http_provider'])
        self.obj_channel_transaction_history_dao = ChannelTransactionStatusDataAccessObject(
            repo=self.repo)

    def manage_channel_transaction_status(self):
        transaction_data = self.obj_channel_transaction_history_dao.get_pending_transaction_data(
        )
        print(transaction_data)
        for transaction_record in transaction_data:
            transaction_hash = transaction_record['transaction_hash']
            transaction_receipt = self.obj_blockchain_util.get_transaction_receipt_from_blockchain(
                transaction_hash=transaction_hash)
            if transaction_receipt is not None:
                status = TransactionStatus.SUCCESS if transaction_receipt.status == 1 else TransactionStatus.FAILED
                self.obj_channel_transaction_history_dao.update_channel_transaction_history(
                    transaction_hash=transaction_hash, status=status)
class OrderService:
    def __init__(self, obj_repo):
        self.repo = obj_repo
        self.obj_transaction_history_dao = TransactionHistoryDAO(self.repo)
        self.lambda_client = boto3.client('lambda', region_name=REGION_NAME)
        self.boto_client = BotoUtils(REGION_NAME)
        self.wallet_service = WalletService()
        self.obj_blockchain_util = BlockChainUtil(
            provider_type="HTTP_PROVIDER",
            provider=NETWORKS[NETWORK_ID]['http_provider']
        )
        self.utils = Utils()

    def initiate_order(self, username, payload_dict):
        """
            Initiate Order
                Step 1  Order Creation
                Step 2  Initiate Payment
                Step 3  Persist Transaction History
        """
        price = payload_dict["price"]
        order_type = payload_dict["item_details"]["order_type"]
        item_details = payload_dict["item_details"]
        group_id = item_details["group_id"]
        org_id = item_details["org_id"]
        channel_id = ""
        amount_in_cogs = self.calculate_amount_in_cogs(amount=price["amount"], currency=price["currency"])
        if amount_in_cogs < 1:
            raise Exception("Amount in cogs should be greater than equal to 1")
        item_details["amount_in_cogs"] = amount_in_cogs

        if order_type == OrderType.CREATE_WALLET_AND_CHANNEL.value:
            item_details["wallet_address"] = ""
            recipient = self.get_payment_address_for_org(group_id=group_id, org_id=org_id)

        elif order_type == OrderType.CREATE_CHANNEL.value:
            recipient = self.get_payment_address_for_org(group_id=group_id, org_id=org_id)

        elif order_type == OrderType.FUND_CHANNEL.value:
            channel = self.get_channel_for_topup(username=username, group_id=group_id, org_id=org_id)
            if channel is None:
                raise Exception(f"Channel not found for the user: {username} with org: {org_id} group: {group_id}")
            recipient = channel["recipient"]
            channel_id = channel["channel_id"]
            item_details["wallet_address"] = channel["address"]

        else:
            raise Exception("Invalid order type")

        item_details["channel_id"] = channel_id
        item_details["recipient"] = recipient
        order_details = self.manage_create_order(
            username=username, item_details=item_details,
            price=price
        )
        order_id = order_details["order_id"]

        try:
            payment_data = self.manage_initiate_payment(
                username=username, order_id=order_id, price=price,
                payment_method=payload_dict["payment_method"]
            )

            payment_id = payment_data["payment_id"]
            raw_payment_data = json.dumps(payment_data["payment"])
            obj_transaction_history = TransactionHistory(
                username=username, order_id=order_id, order_type=order_type,
                payment_id=payment_id, raw_payment_data=raw_payment_data,
                status=Status.PAYMENT_INITIATED.value
            )
            self.obj_transaction_history_dao.insert_transaction_history(obj_transaction_history=obj_transaction_history)
            return payment_data
        except Exception as e:
            obj_transaction_history = TransactionHistory(
                username=username, order_id=order_id, order_type=order_type,
                status=Status.PAYMENT_INITIATION_FAILED.value
            )
            self.obj_transaction_history_dao.insert_transaction_history(obj_transaction_history=obj_transaction_history)
            print(repr(e))
            raise e

    def get_channel_for_topup(self, username, group_id, org_id):
        channel_details = self.wallet_service.get_channel_details(
            username=username, group_id=group_id, org_id=org_id
        )
        wallets = channel_details["wallets"]
        for wallet in wallets:
            if (wallet["type"] == "GENERAL") and len(wallet["channels"]) > 0:
                if wallet["channels"][0]["signer"] == SIGNER_ADDRESS:
                    wallet_address = wallet["address"]
                    channel = wallet["channels"][0]
                    channel["address"] = wallet_address
                    return channel
        return None

    def get_payment_address_for_org(self, org_id, group_id):

        group_details_event = {
            "path": f"/org/{org_id}/group/{quote(group_id, safe='')}",
            "pathParameters": {
                "orgId": org_id,
                "group_id": quote(group_id, safe='')
            },
            "httpMethod": "GET"
        }
        logger.info(f"get_group_for_org request: {org_id} and {group_id}")
        group_details_lambda_response = self.lambda_client.invoke(
            FunctionName=GET_GROUP_FOR_ORG_API_ARN,
            InvocationType='RequestResponse',
            Payload=json.dumps(group_details_event)
        )
        group_details_response = json.loads(group_details_lambda_response.get('Payload').read())
        logger.info(f"get_group_for_org response: {group_details_response}")
        if group_details_response["statusCode"] != 200:
            raise Exception(f"Failed to fetch group details for org_id:{org_id} "
                            f"group_id {group_id}")

        group_details_response_body = json.loads(group_details_response["body"])
        groups = group_details_response_body["data"]["groups"]
        if len(groups) == 0:
            raise Exception(f"Failed to find group {group_id} for org_id: {org_id}")
        return groups[0]["payment"]["payment_address"]

    def calculate_amount_in_cogs(self, amount, currency):
        if currency == "USD":
            amount_in_cogs = round(amount * USD_TO_COGS_CONVERSION_FACTOR)
            return amount_in_cogs
        else:
            raise Exception("Currency %s not supported.", currency)

    def execute_order(self, username, payload_dict):
        """
            Execute Order
                Step 1  Execute Payment
                Step 2  Get Receipient Address
                Step 3  Process Order
                Step 4  Update Transaction History
        """
        order_id = payload_dict["order_id"]
        payment_id = payload_dict["payment_id"]
        order = self.get_order_details_by_order_id(order_id, username)
        payment = None
        for payment_item in order["payments"]:
            if payment_item["payment_id"] == payment_id:
                payment = payment_item
                break

        if payment is None:
            raise Exception(f"Failed to fetch order details for order_id {order_id} \n"
                            f"payment_id {payment_id} \n"
                            f"username{username}")

        order_type = order["item_details"]["order_type"]
        item_details = order["item_details"]
        payment_method = payment["payment_details"]["payment_method"]
        paid_payment_details = payload_dict["payment_details"]
        price = payment["price"]
        status = Status.PAYMENT_EXECUTION_FAILED.value
        self.manage_execute_payment(
            username=username, order_id=order_id, payment_id=payment_id,
            payment_details=paid_payment_details, payment_method=payment_method
        )

        status = Status.PAYMENT_EXECUTED.value
        try:
            status = Status.ORDER_PROCESSING_FAILED.value
            amount_in_cogs = self.calculate_amount_in_cogs(amount=price["amount"], currency=price["currency"])
            if amount_in_cogs < 1:
                raise Exception("Amount in cogs should be greater than equal to 1")
            processed_order_data = self.manage_process_order(
                username=username,
                order_id=order_id, order_type=order_type,
                amount=price["amount"],
                currency=price["currency"], order_data=item_details,
                amount_in_cogs=amount_in_cogs
            )
            status = Status.ORDER_PROCESSED.value
            obj_transaction_history = TransactionHistory(
                username=username, order_id=order_id, order_type=order_type,
                status=status, payment_id=payment_id,
                payment_method=payment_method,
                raw_payment_data=json.dumps(paid_payment_details)
            )
            self.obj_transaction_history_dao.insert_transaction_history(obj_transaction_history=obj_transaction_history)
            processed_order_data["price"] = price
            processed_order_data["item_details"] = item_details
            return processed_order_data

        except Exception as e:
            obj_transaction_history = TransactionHistory(
                username=username, order_id=order_id,
                order_type=order_type, status=status
            )
            self.obj_transaction_history_dao.insert_transaction_history(obj_transaction_history=obj_transaction_history)
            print(repr(e))
            raise e

    def get_order_details_by_order_id(self, order_id, username):
        order_details_event = {
            "path": f"order/{order_id}",
            "pathParameters": {"order_id": order_id},
            "httpMethod": "GET"
        }

        logger.info(f"Requesting order details for order_id {order_id}")
        response = self.lambda_client.invoke(
            FunctionName=ORDER_DETAILS_ORDER_ID_ARN,
            InvocationType='RequestResponse',
            Payload=json.dumps(order_details_event)
        )
        order_details_response = json.loads(response.get('Payload').read())
        if order_details_response["statusCode"] != 200:
            raise Exception(f"Failed to fetch order details for order_id {order_id} username{username}")

        order_details_data = json.loads(order_details_response["body"])
        if order_details_data["username"] != username:
            raise Exception(f"Failed to fetch order details for order_id {order_id} username{username}")
        return order_details_data

    def manage_initiate_payment(self, username, order_id, price, payment_method):
        initiate_payment_event = {
            "pathParameters": {"order_id": order_id},
            "httpMethod": "POST",
            "body": json.dumps({"price": price, "payment_method": payment_method})
        }
        response = self.lambda_client.invoke(
            FunctionName=INITIATE_PAYMENT_SERVICE_ARN,
            InvocationType='RequestResponse',
            Payload=json.dumps(initiate_payment_event)
        )
        initiate_payment_data = json.loads(response.get('Payload').read())
        if initiate_payment_data["statusCode"] == 201:
            return json.loads(initiate_payment_data["body"])
        else:
            logger.error("Error initiating payment for user %s", username)
            raise PaymentInitiateFailed

    def manage_create_order(self, username, item_details, price):
        create_order_event = {
            "path": "/order/create",
            "httpMethod": "POST",
            "body": json.dumps({"price": price, "item_details": item_details, "username": username})
        }
        create_order_service_response = self.boto_client.invoke_lambda(
            lambda_function_arn=CREATE_ORDER_SERVICE_ARN,
            invocation_type='RequestResponse',
            payload=json.dumps(create_order_event)
        )
        logger.info(f"create_order_service_response: {create_order_service_response}")

        if create_order_service_response["statusCode"] == 201:
            return json.loads(create_order_service_response["body"])
        else:
            raise Exception(f"Error creating order for user {username}")

    def manage_execute_payment(self, username, order_id, payment_id, payment_details, payment_method):
        execute_payment_event = {
            "pathParameters": {"order_id": order_id, "payment_id": payment_id},
            "body": json.dumps({"payment_method": payment_method, "payment_details": payment_details})
        }
        response = self.lambda_client.invoke(
            FunctionName=EXECUTE_PAYMENT_SERVICE_ARN,
            InvocationType='RequestResponse',
            Payload=json.dumps(execute_payment_event)
        )
        payment_executed = json.loads(response.get('Payload').read())
        if payment_executed["statusCode"] == 201:
            return payment_executed
        else:
            raise Exception(f"Error executing payment for username {username} against order_id {order_id}")

    def manage_process_order(self, username, order_id, order_type, amount, currency, order_data, amount_in_cogs):
        logger.info(f"Order Data {order_data}")
        group_id = order_data["group_id"]
        org_id = order_data["org_id"]
        recipient = order_data["recipient"]
        channel_id = order_data["channel_id"]
        sender = order_data["wallet_address"]
        if order_type == OrderType.CREATE_WALLET_AND_CHANNEL.value:
            wallet_create_payload = {
                "path": "/wallet",
                "body": json.dumps({"username": username}),
                "httpMethod": "POST"
            }
            wallet_create_lambda_response = self.lambda_client.invoke(
                FunctionName=WALLETS_SERVICE_ARN,
                InvocationType='RequestResponse',
                Payload=json.dumps(wallet_create_payload)
            )
            wallet_create_response = json.loads(wallet_create_lambda_response.get("Payload").read())
            if wallet_create_response["statusCode"] != 200:
                raise Exception("Failed to create wallet")
            wallet_create_response_body = json.loads(wallet_create_response["body"])
            wallet_details = wallet_create_response_body["data"]

            try:
                current_block_no = self.obj_blockchain_util.get_current_block_no()
                # 1 block no is mined in 15 sec on average, setting expiration as 10 years
                expiration = current_block_no + (10 * 365 * 24 * 60 * 4)
                message_nonce = current_block_no
                self.EXECUTOR_WALLET_ADDRESS = self.boto_client.get_ssm_parameter(EXECUTOR_ADDRESS)
                group_id_in_hex = "0x" + base64.b64decode(group_id).hex()
                signature_details = self.generate_signature_for_open_channel_for_third_party(
                    recipient=recipient, group_id=group_id_in_hex,
                    amount_in_cogs=amount_in_cogs, expiration=expiration,
                    message_nonce=message_nonce, sender_private_key=wallet_details["private_key"],
                    executor_wallet_address=self.EXECUTOR_WALLET_ADDRESS
                )

                logger.info(f"Signature Details {signature_details}")
                open_channel_body = {
                    'order_id': order_id,
                    'sender': wallet_details["address"],
                    'signature': signature_details["signature"],
                    'r': signature_details["r"],
                    's': signature_details["s"],
                    'v': signature_details["v"],
                    'group_id': group_id,
                    'org_id': org_id,
                    'amount': amount,
                    'currency': currency,
                    'recipient': recipient,
                    'current_block_no': current_block_no,
                    'amount_in_cogs': amount_in_cogs
                }
                channel_details = self.wallet_service.create_channel(open_channel_body=open_channel_body)
                channel_details.update(wallet_details)
                return channel_details
            except Exception as e:
                logger.error("Failed to create channel")
                logger.error(repr(e))
                response = {
                    "transaction_hash": "",
                    "signature": "",
                    "amount_in_cogs": 0,
                    "price": {
                        "amount": amount,
                        "currency": currency
                    },
                    "item_details": order_data
                }
                response.update(wallet_details)
                raise ChannelCreationFailed("Failed to create channel", wallet_details=response)

        elif order_type == OrderType.CREATE_CHANNEL.value:
            try:
                logger.info(f"Order Data {order_data}")
                signature = order_data["signature"]
                v, r, s = Web3.toInt(hexstr="0x" + signature[-2:]), signature[:66], "0x" + signature[66:130]
                open_channel_body = {
                    'order_id': order_id, 'sender': order_data["wallet_address"],
                    'signature': order_data["signature"], 'r': r, 's': s, 'v': v,
                    'group_id': group_id, 'org_id': org_id, 'amount': amount, 'currency': currency,
                    'recipient': recipient, 'current_block_no': order_data["current_block_number"],
                    'amount_in_cogs': amount_in_cogs
                }
                channel_details = self.wallet_service.create_channel(open_channel_body=open_channel_body)
                logger.info("channel_details: ", channel_details)
                return channel_details
            except Exception as e:
                logger.error("Failed to create channel")
                logger.error(repr(e))
                raise ChannelCreationFailed("Failed to create channel", wallet_details=order_data)

        elif order_type == OrderType.FUND_CHANNEL.value:

            try:
                fund_channel_body = {
                    'order_id': order_id,
                    'group_id': group_id,
                    'org_id': org_id,
                    'amount': amount,
                    'channel_id': channel_id,
                    'currency': currency,
                    'recipient': recipient,
                    'sender': sender,
                    'amount_in_cogs': amount_in_cogs
                }
                fund_channel_payload = {
                    "path": "/wallet/channel/deposit",
                    "body": json.dumps(fund_channel_body),
                    "httpMethod": "POST"
                }

                fund_channel_lambda_response = self.lambda_client.invoke(
                    FunctionName=WALLETS_SERVICE_ARN,
                    InvocationType='RequestResponse',
                    Payload=json.dumps(fund_channel_payload)
                )

                fund_channel_response = json.loads(fund_channel_lambda_response.get("Payload").read())
                if fund_channel_response["statusCode"] != 200:
                    raise Exception(f"Failed to add funds in channel for {fund_channel_body}")

                fund_channel_response_body = json.loads(fund_channel_response["body"])
                fund_channel_transaction_details = fund_channel_response_body["data"]
                return fund_channel_transaction_details
            except Exception as e:
                logger.error("Failed to fund channel")
                logger.error(repr(e))
                raise FundChannelFailed()
        else:
            raise Exception("Order type is not valid.")

    def get_order_details_by_username(self, username):
        order_details_event = {
            "path": f"/order",
            "queryStringParameters": {"username": username},
            "httpMethod": "GET"
        }

        logger.info(f"Requesting order details for username {username}")
        order_details_response = self.boto_client.invoke_lambda(
            lambda_function_arn=ORDER_DETAILS_BY_USERNAME_ARN,
            invocation_type='RequestResponse',
            payload=json.dumps(order_details_event)
        )
        if order_details_response["statusCode"] != 200:
            raise Exception(f"Failed to fetch order details for username{username}")

        org_id_name_mapping = self.get_organizations_from_contract()

        order_details_response_body = json.loads(order_details_response["body"])
        orders = order_details_response_body["orders"]

        for order in orders:
            order_id = order["order_id"]
            order["wallet_type"] = "GENERAL"
            if "org_id" in order["item_details"]:
                org_id = order["item_details"]["org_id"]
                if org_id in org_id_name_mapping:
                    order["item_details"]["organization_name"] = org_id_name_mapping[org_id]

            transaction_details_event = {
                "path": f"/wallet/channel/transactions",
                "queryStringParameters": {"order_id": order_id},
                "httpMethod": "GET"
            }
            transaction_details_lambda_response = self.lambda_client.invoke(
                FunctionName=WALLETS_SERVICE_ARN,
                InvocationType='RequestResponse',
                Payload=json.dumps(transaction_details_event)
            )
            transaction_details_response = json.loads(transaction_details_lambda_response.get('Payload').read())
            if transaction_details_response["statusCode"] != 200:
                raise Exception(f"Failed to fetch transaction details for username{order_id}")
            transaction_details_response_body = json.loads(transaction_details_response["body"])
            order["wallet_transactions"] = transaction_details_response_body["data"]["transactions"]
            order_status = TransactionStatus.SUCCESS
            for payment in order["payments"]:
                if payment["payment_status"] != TransactionStatus.SUCCESS:
                    order_status = payment["payment_status"]
                    break

            for wallet_transaction in order["wallet_transactions"]:
                if wallet_transaction["status"] != TransactionStatus.SUCCESS:
                    order_status = wallet_transaction["status"]
                    break

            order["order_status"] = order_status
        return {"orders": orders}

    def get_organizations_from_contract(self):
        org_details_event = {
            "path": f"/org",
            "httpMethod": "GET"
        }
        org_details_response = self.boto_client.invoke_lambda(
            lambda_function_arn=GET_ALL_ORG_API_ARN,
            invocation_type='RequestResponse',
            payload=json.dumps(org_details_event)
        )
        if org_details_response["statusCode"] != 200:
            raise Exception("Failed to get org details")

        org_details = json.loads(org_details_response["body"])["data"]
        org_id_name_mapping = {}
        for org in org_details:
            org_id_name_mapping[org["org_id"]] = org["org_name"]

        return org_id_name_mapping

    def generate_signature_for_open_channel_for_third_party(self, recipient, group_id, amount_in_cogs, expiration,
                                                            message_nonce, sender_private_key, executor_wallet_address):

        signature_for_open_channel_for_third_party_body = {
            'recipient': recipient,
            'group_id': group_id,
            'amount_in_cogs': amount_in_cogs,
            'expiration': expiration,
            'message_nonce': message_nonce,
            'signer_key': sender_private_key,
            'executor_wallet_address': executor_wallet_address
        }

        signature_for_open_channel_for_third_party_payload = {
            "path": "/signer/open-channel-for-third-party",
            "body": json.dumps(signature_for_open_channel_for_third_party_body),
            "httpMethod": "POST"
        }

        signature_for_open_channel_for_third_party_response = self.lambda_client.invoke(
            FunctionName=SIGNER_SERVICE_ARN,
            InvocationType='RequestResponse',
            Payload=json.dumps(signature_for_open_channel_for_third_party_payload)
        )

        signature_response = json.loads(signature_for_open_channel_for_third_party_response.get("Payload").read())
        if signature_response["statusCode"] != 200:
            raise Exception(f"Failed to create signature for {signature_for_open_channel_for_third_party_body}")
        signature_details = json.loads(signature_response["body"])
        return signature_details["data"]

    def cancel_order(self):
        logger.info("Start of UpdateTransactionStatus::manage_update_canceled_order_in_txn_history")
        list_of_order_id_for_expired_transaction = self.obj_transaction_history_dao.get_order_id_for_expired_transaction()
        logger.info(f"List of order_id to be updated with ORDER CANCELED: {list_of_order_id_for_expired_transaction}")
        update_transaction_status = self.obj_transaction_history_dao.update_transaction_status(
            list_of_order_id=list_of_order_id_for_expired_transaction, status=OrderStatus.ORDER_CANCELED.value)
        return update_transaction_status

    def cancel_order_for_given_order_id(self, order_id):
        logger.info("UpdateTransactionStatus::cancel_order_for_given_order_id: %s", order_id)
        transaction_data_dict = self.obj_transaction_history_dao.get_transaction_details_for_given_order_id(
            order_id=order_id)
        if transaction_data_dict["status"] == OrderStatus.ORDER_CANCELED.value:
            return f"Order with order_id {order_id} is already canceled."
        elif transaction_data_dict["status"] in [OrderStatus.PAYMENT_INITIATED.value,
                                                 OrderStatus.PAYMENT_INITIATION_FAILED.value,
                                                 OrderStatus.PAYMENT_EXECUTION_FAILED]:
            self.obj_transaction_history_dao.update_transaction_status(list_of_order_id=[order_id],
                                                                       status=OrderStatus.ORDER_CANCELED.value)
            return f"Order with order_id {order_id} is canceled successfully."
        else:
            return f"Unable to cancel order with order_id {order_id}"

    def currency_to_token(self, amount, currency):
        amount_in_cogs = self.calculate_amount_in_cogs(amount=decimal.Decimal(amount), currency=currency)
        conversion_data = {"base": currency, "amount": amount, "amount_in_cogs": str(amount_in_cogs),
                           "amount_in_agi": str(self.utils.cogs_to_agi(cogs=amount_in_cogs)),
                           f"{currency}/cogs": str(USD_TO_COGS_CONVERSION_FACTOR), "agi/cogs": str(COGS_TO_AGI)}
        logger.debug(f"currency_to_token::conversion_data {conversion_data}")
        return conversion_data
Ejemplo n.º 17
0
 def __init__(self, obj_repo):
     self.repo = obj_repo
     self.obj_util = Utils()
     self.blockchain_util = BlockChainUtil(
         provider_type="WS_PROVIDER",
         provider=NETWORKS[NETWORK_ID]["ws_provider"])
Ejemplo n.º 18
0
import json

from common.blockchain_util import BlockChainUtil
from common.boto_utils import BotoUtils
from common.constant import TransactionStatus
from common.logger import get_logger
from wallets.config import NETWORKS, NETWORK_ID, REGION_NAME, GET_RAW_EVENT_DETAILS
from wallets.infrastructure.repositories.channel_repository import ChannelRepository

boto_utils = BotoUtils(region_name=REGION_NAME)
channel_repo = ChannelRepository()
logger = get_logger(__name__)
obj_blockchain_util = BlockChainUtil(
    provider_type="HTTP_PROVIDER",
    provider=NETWORKS[NETWORK_ID]['http_provider'])


class ChannelTransactionStatusService:
    def __init__(self):
        pass

    @staticmethod
    def get_mpe_processed_transactions_from_event_pub_sub(transaction_list):
        response = boto_utils.invoke_lambda(
            payload=json.dumps({
                "transaction_hash_list": transaction_list,
                "contract_name": "MPE"
            }),
            lambda_function_arn=GET_RAW_EVENT_DETAILS,
            invocation_type="RequestResponse")
        if response["statusCode"] != 200:
 def __init__(self, ws_provider, ipfs_url, ipfs_port):
     self._blockchain_util = BlockChainUtil("WS_PROVIDER", ws_provider)
     self._s3_util = S3Util(S3_BUCKET_ACCESS_KEY, S3_BUCKET_SECRET_KEY)
     self._ipfs_util = IPFSUtil(ipfs_url, ipfs_port)
class WalletService:
    def __init__(self, obj_repo):
        self.obj_repo = obj_repo

    def create_and_register_wallet(self):
        self.obj_blockchain_util = BlockChainUtil(
            provider_type="HTTP_PROVIDER",
            provider=NETWORKS[NETWORK_ID]['http_provider'])
        address, private_key = self.obj_blockchain_util.create_account()
        obj_wallet = Wallet(address=address,
                            private_key=private_key,
                            type=GENERAL_WALLET_TYPE,
                            status=0)
        registered = self.register_wallet(obj_wallet=obj_wallet)
        if registered:
            return obj_wallet.get_wallet()
        raise Exception("Unable to create and register wallet.")

    def register_wallet(self, obj_wallet):
        wallet_details = obj_wallet.get_wallet()
        obj_wallet_dao = WalletDAO(obj_repo=self.obj_repo)
        persisted = obj_wallet_dao.insert_wallet_details(
            address=wallet_details["address"],
            type=wallet_details["type"],
            status=wallet_details["status"])
        if persisted:
            return True
        raise Exception("Unable to register wallet.")

    def __generate_signature_details(self, recipient, group_id, agi_tokens,
                                     expiration, message_nonce, signer_key):
        data_types = [
            "string", "address", "address", "address", "address", "bytes32",
            "uint256", "uint256", "uint256"
        ]
        values = [
            "__openChannelByThirdParty", self.mpe_address,
            EXECUTOR_WALLET_ADDRESS, SIGNER_ADDRESS, recipient, group_id,
            agi_tokens, expiration, message_nonce
        ]
        signature = self.obj_blockchain_util.generate_signature(
            data_types=data_types, values=values, signer_key=signer_key)
        v, r, s = Web3.toInt(
            hexstr="0x" +
            signature[-2:]), signature[:66], "0x" + signature[66:130]
        return r, s, v, signature

    def __calculate_agi_tokens(self, amount, currency):
        if currency == "USD":
            agi_tokens = amount
        else:
            raise Exception("Currency %s not supported.", currency)

        return agi_tokens

    def open_channel_by_third_party(self, order_id, sender, sender_private_key,
                                    group_id, amount, currency, recipient):
        obj_wallet_dao = WalletDAO(obj_repo=self.obj_repo)
        self.obj_blockchain_util = BlockChainUtil(
            provider_type="HTTP_PROVIDER",
            provider=NETWORKS[NETWORK_ID]['http_provider'])
        method_name = "openChannelByThirdParty"
        self.mpe_address = self.obj_blockchain_util.read_contract_address(
            net_id=NETWORK_ID, path=MPE_ADDR_PATH, key='address')
        current_block_no = self.obj_blockchain_util.get_current_block_no()
        # 1 block no is mined in 15 sec on average, setting expiration as 10 years
        expiration = current_block_no + (10 * 365 * 24 * 60 * 4)
        agi_tokens = self.__calculate_agi_tokens(amount=amount,
                                                 currency=currency)
        group_id_in_hex = "0x" + base64.b64decode(group_id).hex()
        r, s, v, signature = self.__generate_signature_details(
            recipient=recipient,
            group_id=group_id_in_hex,
            agi_tokens=agi_tokens,
            expiration=expiration,
            message_nonce=current_block_no,
            signer_key=sender_private_key)
        positional_inputs = (sender, SIGNER_ADDRESS, recipient,
                             group_id_in_hex, agi_tokens, expiration,
                             current_block_no, v, r, s)
        transaction_object = self.obj_blockchain_util.create_transaction_object(
            *positional_inputs,
            method_name=method_name,
            address=EXECUTOR_WALLET_ADDRESS,
            contract_path=MPE_CNTRCT_PATH,
            contract_address_path=MPE_ADDR_PATH,
            net_id=NETWORK_ID)
        raw_transaction = self.obj_blockchain_util.sign_transaction_with_private_key(
            transaction_object=transaction_object,
            private_key=EXECUTOR_WALLET_KEY)
        transaction_hash = self.obj_blockchain_util.process_raw_transaction(
            raw_transaction=raw_transaction)
        print("openChannelByThirdParty::transaction_hash", transaction_hash)
        obj_wallet_dao.insert_channel_history(
            order_id=order_id,
            amount=amount,
            currency=currency,
            type=method_name,
            address=sender,
            signature=signature,
            request_parameters=str(positional_inputs),
            transaction_hash=transaction_hash,
            status=0)

        return {
            "transaction_hash": transaction_hash,
            "signature": signature,
            "agi_tokens": agi_tokens,
            "positional_inputs": positional_inputs,
            "type": method_name
        }

    def update_wallet_status(self, address):
        pass

    def add_funds_to_channel(self, order_id, channel_id, amount, currency):
        method_name = "channelAddFunds"
        agi_tokens = self.__calculate_agi_tokens(amount=amount,
                                                 currency=currency)
        positional_inputs = (channel_id, agi_tokens)
        transaction_object = self.obj_utils.create_transaction_object(
            *positional_inputs,
            method_name=method_name,
            address=EXECUTOR_WALLET_ADDRESS,
            contract_path=MPE_CNTRCT_PATH,
            contract_address_path=MPE_ADDR_PATH,
            net_id=self.net_id)
        raw_transaction = self.obj_utils.sign_transaction_with_private_key(
            transaction_object=transaction_object,
            private_key=EXECUTOR_WALLET_KEY)

        transaction_hash = self.obj_utils.process_raw_transaction(
            raw_transaction=raw_transaction)
        print("channelAddFunds::transaction_hash", transaction_hash)
        return {
            "transaction_hash": transaction_hash,
            "agi_tokens": agi_tokens,
            "positional_inputs": positional_inputs,
            "type": method_name
        }
Ejemplo n.º 21
0
class Signer:
    def __init__(self, net_id):
        self.net_id = net_id
        self.lambda_client = boto3.client("lambda", region_name=REGION_NAME)
        self.obj_utils = Utils()
        self.obj_blockchain_utils = BlockChainUtil(
            provider_type="HTTP_PROVIDER",
            provider=NETWORKS[self.net_id]["http_provider"],
        )
        self.mpe_address = self.obj_blockchain_utils.read_contract_address(
            net_id=self.net_id, path=MPE_ADDR_PATH, key="address")
        self.current_block_no = self.obj_blockchain_utils.get_current_block_no(
        )

    def _get_free_calls_allowed(self, org_id, service_id, group_id):
        lambda_payload = {
            "httpMethod": "GET",
            "pathParameters": {
                "orgId": org_id,
                "serviceId": service_id
            },
        }
        response = self.lambda_client.invoke(
            FunctionName=
            GET_SERVICE_DETAILS_FOR_GIVEN_ORG_ID_AND_SERVICE_ID_ARN,
            InvocationType="RequestResponse",
            Payload=json.dumps(lambda_payload),
        )
        response_body_raw = json.loads(response.get("Payload").read())["body"]
        get_service_response = json.loads(response_body_raw)
        if get_service_response["status"] == "success":
            groups_data = get_service_response["data"].get("groups", [])
            for group_data in groups_data:
                if group_data["group_id"] == group_id:
                    return group_data["free_calls"]
        raise Exception(
            "Unable to fetch free calls information for service %s under organization %s for %s group.",
            service_id, org_id, group_id)

    def _get_total_calls_made(self, username, org_id, service_id, group_id):
        lambda_payload = {
            "httpMethod": "GET",
            "queryStringParameters": {
                "organization_id": org_id,
                "service_id": service_id,
                "username": username,
                "group_id": group_id,
            },
        }
        response = self.lambda_client.invoke(
            FunctionName=METERING_ARN,
            InvocationType="RequestResponse",
            Payload=json.dumps(lambda_payload),
        )
        if response["StatusCode"] == 200:
            metering_data_raw = json.loads(
                response.get("Payload").read())["body"]
            total_calls_made = json.loads(metering_data_raw).get(
                "total_calls_made", None)
            if total_calls_made is not None:
                return total_calls_made
        raise Exception(
            "Unable to fetch total calls made for service %s under organization %s for %s group.",
            service_id, org_id, group_id)

    def _get_no_of_free_call_available(self, username, org_id, service_id,
                                       group_id):

        token_to_get_free_call, expiry_date_block, signature, current_block_number, daemon_endpoint, free_calls_allowed = self.token_to_get_free_call(
            username, org_id, service_id, group_id)
        total_free_call_available = 0
        try:

            total_free_call_available = self._get_no_of_free_calls_from_daemon(
                username, token_to_get_free_call, expiry_date_block, signature,
                current_block_number, daemon_endpoint)
        except Exception as e:
            logger.info(
                f"Free call from daemon not available switching to metering {org_id} {service_id} {group_id} {username}"
            )
            total_free_call_made = self._get_total_calls_made(
                username, org_id, service_id, group_id)

            total_free_call_available = free_calls_allowed - total_free_call_made

        return total_free_call_available

    def _free_calls_allowed(self, username, org_id, service_id, group_id):
        """
            Method to check free calls exists for given user or not.
            Call monitoring service to get the details
        """

        free_calls_available = self._get_no_of_free_call_available(
            username, org_id, service_id, group_id)
        if free_calls_available > 0:
            return True

        return False

    def signature_for_free_call(self, user_data, org_id, service_id, group_id):
        """
            Method to generate signature for free call.
        """
        try:
            username = user_data["authorizer"]["claims"]["email"]
            if self._free_calls_allowed(username=username,
                                        org_id=org_id,
                                        service_id=service_id,
                                        group_id=group_id):
                current_block_no = self.obj_utils.get_current_block_no(
                    ws_provider=NETWORKS[self.net_id]["ws_provider"])
                provider = Web3.HTTPProvider(
                    NETWORKS[self.net_id]["http_provider"])
                w3 = Web3(provider)
                message = web3.Web3.soliditySha3(
                    ["string", "string", "string", "string", "uint256"],
                    [
                        PREFIX_FREE_CALL, username, org_id, service_id,
                        current_block_no
                    ],
                )
                signer_key = SIGNER_KEY
                if not signer_key.startswith("0x"):
                    signer_key = "0x" + signer_key
                signature = bytes(
                    w3.eth.account.signHash(defunct_hash_message(message),
                                            signer_key).signature)
                signature = signature.hex()
                if not signature.startswith("0x"):
                    signature = "0x" + signature
                return {
                    "snet-free-call-user-id": username,
                    "snet-payment-channel-signature-bin": signature,
                    "snet-current-block-number": current_block_no,
                    "snet-payment-type": "free-call",
                    "snet-free-call-auth-token-bin": "",
                    "snet-free-call-token-expiry-block": 0
                }
            else:
                raise Exception("Free calls expired for username %s.",
                                username)
        except Exception as e:
            logger.error(repr(e))
            raise e

    def signature_for_regular_call(self, user_data, channel_id, nonce, amount):
        """
            Method to generate signature for regular call.
        """
        try:
            username = user_data["authorizer"]["claims"]["email"]
            data_types = ["string", "address", "uint256", "uint256", "uint256"]
            values = [
                "__MPE_claim_message",
                self.mpe_address,
                channel_id,
                nonce,
                amount,
            ]
            signature = self.obj_blockchain_utils.generate_signature(
                data_types=data_types, values=values, signer_key=SIGNER_KEY)
            return {
                "snet-payment-channel-signature-bin": signature,
                "snet-payment-type": "escrow",
                "snet-payment-channel-id": channel_id,
                "snet-payment-channel-nonce": nonce,
                "snet-payment-channel-amount": amount,
                "snet-current-block-number": self.current_block_no,
            }
        except Exception as e:
            logger.error(repr(e))
            raise Exception(
                "Unable to generate signature for daemon call for username %s",
                username)

    def signature_for_state_service(self, user_data, channel_id):
        """
            Method to generate signature for state service.
        """
        try:
            username = user_data["authorizer"]["claims"]["email"]
            data_types = ["string", "address", "uint256", "uint256"]
            values = [
                "__get_channel_state",
                self.mpe_address,
                channel_id,
                self.current_block_no,
            ]
            signature = self.obj_blockchain_utils.generate_signature(
                data_types=data_types, values=values, signer_key=SIGNER_KEY)
            return {
                "signature": signature,
                "snet-current-block-number": self.current_block_no,
            }
        except Exception as e:
            logger.error(repr(e))
            raise Exception(
                "Unable to generate signature for daemon call for username %s",
                username)

    def signature_for_open_channel_for_third_party(self, recipient, group_id,
                                                   amount_in_cogs, expiration,
                                                   message_nonce,
                                                   sender_private_key,
                                                   executor_wallet_address):
        data_types = [
            "string", "address", "address", "address", "address", "bytes32",
            "uint256", "uint256", "uint256"
        ]
        values = [
            "__openChannelByThirdParty", self.mpe_address,
            executor_wallet_address, SIGNER_ADDRESS, recipient, group_id,
            amount_in_cogs, expiration, message_nonce
        ]
        signature = self.obj_blockchain_utils.generate_signature(
            data_types=data_types,
            values=values,
            signer_key=sender_private_key)
        v, r, s = Web3.toInt(
            hexstr="0x" +
            signature[-2:]), signature[:66], "0x" + signature[66:130]
        return {"r": r, "s": s, "v": v, "signature": signature}

    def _get_no_of_free_calls_from_daemon(self, email, token_to_get_free_call,
                                          expiry_date_block, signature,
                                          current_block_number,
                                          daemon_endpoint):

        request = state_service_pb2.FreeCallStateRequest()
        request.user_id = email
        request.token_for_free_call = token_to_get_free_call
        request.token_expiry_date_block = expiry_date_block
        request.signature = signature
        request.current_block = current_block_number

        endpoint_object = urlparse(daemon_endpoint)
        if endpoint_object.port is not None:
            channel_endpoint = endpoint_object.hostname + ":" + str(
                endpoint_object.port)
        else:
            channel_endpoint = endpoint_object.hostname

        if endpoint_object.scheme == "http":
            channel = grpc.insecure_channel(channel_endpoint)
        elif endpoint_object.scheme == "https":
            channel = grpc.secure_channel(channel_endpoint,
                                          grpc.ssl_channel_credentials())
        else:
            raise ValueError(
                'Unsupported scheme in service metadata ("{}")'.format(
                    endpoint_object.scheme))

        stub = state_service_pb2_grpc.FreeCallStateServiceStub(channel)
        response = stub.GetFreeCallsAvailable(request)

        return response.free_calls_available

    def _is_free_call_available(self, email, token_for_free_call,
                                expiry_date_block, signature,
                                current_block_number, daemon_endpoint):
        if self._get_no_of_free_calls_from_daemon(
                email, token_for_free_call, expiry_date_block, signature,
                current_block_number, daemon_endpoint) > 0:
            return True
        return False

    def _get_daemon_endpoint_and_free_call_for_group(self, org_id, service_id,
                                                     group_id):
        lambda_payload = {
            "httpMethod": "GET",
            "pathParameters": {
                "orgId": org_id,
                "serviceId": service_id
            },
        }
        response = self.lambda_client.invoke(
            FunctionName=
            GET_SERVICE_DETAILS_FOR_GIVEN_ORG_ID_AND_SERVICE_ID_ARN,
            InvocationType="RequestResponse",
            Payload=json.dumps(lambda_payload),
        )
        response_body_raw = json.loads(response.get("Payload").read())["body"]
        get_service_response = json.loads(response_body_raw)
        if get_service_response["status"] == "success":
            groups_data = get_service_response["data"].get("groups", [])
            for group_data in groups_data:
                if group_data["group_id"] == group_id:
                    return group_data["endpoints"][0][
                        "endpoint"], group_data.get("free_calls", 0)
        raise Exception(
            "Unable to fetch daemon Endpoint information for service %s under organization %s for %s group.",
            service_id, org_id, group_id)

    def token_to_get_free_call(self, email, org_id, service_id, group_id):
        signer_public_key_checksum = Web3.toChecksumAddress(SIGNER_ADDRESS)
        current_block_number = self.obj_blockchain_utils.get_current_block_no()
        expiry_date_block = current_block_number + FREE_CALL_EXPIRY
        token_to_get_free_call = self.obj_blockchain_utils.generate_signature_bytes(
            ["string", "address", "uint256"],
            [email, signer_public_key_checksum, expiry_date_block], SIGNER_KEY)

        signature = self.obj_blockchain_utils.generate_signature_bytes([
            "string", "string", "string", "string", "string", "uint256",
            "bytes32"
        ], [
            "__prefix_free_trial", email, org_id, service_id, group_id,
            current_block_number, token_to_get_free_call
        ], SIGNER_KEY)

        daemon_endpoint, free_calls_allowed = self._get_daemon_endpoint_and_free_call_for_group(
            org_id, service_id, group_id)
        logger.info(
            f"Got daemon endpoint {daemon_endpoint} for org {org_id} service {service_id} group {group_id}"
        )

        return token_to_get_free_call, expiry_date_block, signature, current_block_number, daemon_endpoint, free_calls_allowed

    def token_to_make_free_call(self, email, org_id, service_id, group_id,
                                user_public_key):
        token_to_get_free_call, expiry_date_block, signature, current_block_number, daemon_endpoint, free_calls_allowed = self.token_to_get_free_call(
            email, org_id, service_id, group_id)

        token_with_expiry_to_make_free_call = ""
        if self._is_free_call_available(email, token_to_get_free_call,
                                        expiry_date_block, signature,
                                        current_block_number, daemon_endpoint):
            token_with_expiry_to_make_free_call = self.obj_blockchain_utils.generate_signature_bytes(
                ["string", "address", "uint256"], [
                    email,
                    Web3.toChecksumAddress(user_public_key), expiry_date_block
                ], SIGNER_KEY)

        return {
            "token_to_make_free_call":
            token_with_expiry_to_make_free_call.hex(),
            "token_expiration_block": expiry_date_block
        }
Ejemplo n.º 22
0
class TestUtils(unittest.TestCase):
    def setUp(self):
        self.net_id = 3
        self.http_provider = Web3.HTTPProvider(
            NETWORKS[self.net_id]['http_provider'])
        self.obj_utils = BlockChainUtil(provider_type="HTTP_PROVIDER",
                                        provider=self.http_provider)
        self.mpe_address = self.obj_utils.read_contract_address(
            net_id=self.net_id, path=MPE_ADDR_PATH, key='address')
        self.recipient = "0x9c302750c50307D3Ad88eaA9a6506874a15cE4Cb"
        self.group_id = "0x" + base64.b64decode(
            "DS2OoKSfGE/7hAO/023psl4Qdj0MJvYsreJbCoAHVSc=").hex()
        self.agi_tokens = 1
        self.current_block_no = self.obj_utils.get_current_block_no()
        self.expiration = self.current_block_no + 10000000
        self.channel_id = 0

    def generate_signature(self, message_nonce, signer_key):
        data_types = [
            "string", "address", "address", "address", "address", "bytes32",
            "uint256", "uint256", "uint256"
        ]
        values = [
            "__openChannelByThirdParty", self.mpe_address, EXECUTOR_ADDRESS,
            SIGNER_ADDRESS, self.recipient, self.group_id, self.agi_tokens,
            self.expiration, message_nonce
        ]
        signature = self.obj_utils.generate_signature(data_types=data_types,
                                                      values=values,
                                                      signer_key=signer_key)
        v, r, s = Web3.toInt(
            hexstr="0x" +
            signature[-2:]), signature[:66], "0x" + signature[66:130]
        assert (v == 27 or v == 28)
        assert (len(r) == 66)
        assert (len(s) == 66)
        assert (len(signature) == 132)
        return r, s, v, signature

    def test_create_account(self):
        address, private_key = self.obj_utils.create_account()
        assert (Web3.isAddress(address) == True)
        return address, private_key

    def test_create_transaction_object1(self):
        method_name = "openChannelByThirdParty"
        sender, sender_private_key = self.test_create_account()
        message_nonce = self.obj_utils.get_current_block_no()
        r, s, v, signature = self.generate_signature(
            message_nonce=message_nonce, signer_key=sender_private_key)
        positional_inputs = (sender, SIGNER_ADDRESS, self.recipient,
                             self.group_id, self.agi_tokens, self.expiration,
                             message_nonce, v, r, s)
        transaction_object = self.obj_utils.create_transaction_object(
            *positional_inputs,
            method_name=method_name,
            address=EXECUTOR_ADDRESS,
            contract_path=MPE_CNTRCT_PATH,
            contract_address_path=MPE_ADDR_PATH,
            net_id=self.net_id)
        print(transaction_object)
        raw_transaction = self.obj_utils.sign_transaction_with_private_key(
            transaction_object=transaction_object, private_key=EXECUTOR_KEY)
        transaction_hash = self.obj_utils.process_raw_transaction(
            raw_transaction=raw_transaction)
        print("openChannelByThirdParty::transaction_hash", transaction_hash)

    def test_create_transaction_object2(self):
        method_name = "channelAddFunds"
        positional_inputs = (self.channel_id, self.agi_tokens)
        transaction_object = self.obj_utils.create_transaction_object(
            *positional_inputs,
            method_name=method_name,
            address=EXECUTOR_ADDRESS,
            contract_path=MPE_CNTRCT_PATH,
            contract_address_path=MPE_ADDR_PATH,
            net_id=self.net_id)
        print(transaction_object)
        raw_transaction = self.obj_utils.sign_transaction_with_private_key(
            transaction_object=transaction_object, private_key=EXECUTOR_KEY)

        transaction_hash = self.obj_utils.process_raw_transaction(
            raw_transaction=raw_transaction)
        print("channelAddFunds::transaction_hash", transaction_hash)
class OrganizationEventConsumer(EventConsumer):
    _connection = Repository(NETWORK_ID, NETWORKS=NETWORKS)
    _organization_repository = OrganizationRepository(_connection)
    _service_repository = ServiceRepository(_connection)

    def __init__(self, ws_provider, ipfs_url, ipfs_port):
        self._ipfs_util = IPFSUtil(ipfs_url, ipfs_port)
        self._blockchain_util = BlockChainUtil("WS_PROVIDER", ws_provider)
        self._s3_util = S3Util(S3_BUCKET_ACCESS_KEY, S3_BUCKET_SECRET_KEY)

    def on_event(self, event):
        pass

    def _push_asset_to_s3_using_hash(self, hash, org_id, service_id):
        io_bytes = self._ipfs_util.read_bytesio_from_ipfs(
            hash.lstrip("ipfs://"))
        filename = hash.split("/")[1]
        if service_id:
            s3_filename = ASSETS_PREFIX + "/" + org_id + "/" + service_id + "/" + filename
        else:
            s3_filename = ASSETS_PREFIX + "/" + org_id + "/" + filename

        new_url = self._s3_util.push_io_bytes_to_s3(s3_filename,
                                                    ASSETS_BUCKET_NAME,
                                                    io_bytes)
        return new_url

    def _get_new_assets_url(self, org_id, new_ipfs_data):
        new_assets_hash = new_ipfs_data.get('assets', {})
        existing_assets_hash = {}
        existing_assets_url = {}

        existing_organization = self._organization_repository.get_organization(
            org_id)
        if existing_organization:
            existing_assets_hash = json.loads(
                existing_organization["assets_hash"])
            existing_assets_url = json.loads(
                existing_organization["org_assets_url"])
        new_assets_url_mapping = self._comapre_assets_and_push_to_s3(
            existing_assets_hash, new_assets_hash, existing_assets_url, org_id,
            "")
        return new_assets_url_mapping

    def _get_org_id_from_event(self, event):
        event_org_data = eval(event['data']['json_str'])
        org_id_bytes = event_org_data['orgId']
        org_id = Web3.toText(org_id_bytes).rstrip("\x00")
        return org_id

    def _get_registry_contract(self):
        net_id = NETWORK_ID
        base_contract_path = os.path.abspath(
            os.path.join(os.path.dirname(__file__), '..', '..', 'node_modules',
                         'singularitynet-platform-contracts'))
        registry_contract = self._blockchain_util.get_contract_instance(
            base_contract_path, "REGISTRY", net_id)

        return registry_contract

    def _get_org_details_from_blockchain(self, event):
        logger.info(f"processing org event {event}")

        registry_contract = self._get_registry_contract()
        org_id = self._get_org_id_from_event(event)

        blockchain_org_data = registry_contract.functions.getOrganizationById(
            org_id.encode('utf-8')).call()
        org_metadata_uri = Web3.toText(
            blockchain_org_data[2]).rstrip("\x00").lstrip("ipfs://")
        ipfs_org_metadata = self._ipfs_util.read_file_from_ipfs(
            org_metadata_uri)

        return org_id, blockchain_org_data, ipfs_org_metadata, org_metadata_uri
class TestUtils(unittest.TestCase):
    def setUp(self):
        self.net_id = 3
        self.http_provider = Web3.HTTPProvider(
            NETWORKS[self.net_id]['http_provider'])
        self.obj_utils = BlockChainUtil(provider_type="HTTP_PROVIDER",
                                        provider=self.http_provider)
        self.mpe_address = self.obj_utils.read_contract_address(
            net_id=self.net_id, path=MPE_ADDR_PATH, key='address')
        self.recipient = "0x9c302750c50307D3Ad88eaA9a6506874a15cE4Cb"
        self.group_id = "0x" + base64.b64decode(
            "DS2OoKSfGE/7hAO/023psl4Qdj0MJvYsreJbCoAHVSc=").hex()
        self.agi_tokens = 1
        self.current_block_no = self.obj_utils.get_current_block_no()
        self.expiration = self.current_block_no + 10000000
        self.channel_id = 0

    def generate_signature(self, message_nonce, signer_key):
        data_types = [
            "string", "address", "address", "address", "address", "bytes32",
            "uint256", "uint256", "uint256"
        ]
        values = [
            "__openChannelByThirdParty", self.mpe_address, EXECUTOR_ADDRESS,
            SIGNER_ADDRESS, self.recipient, self.group_id, self.agi_tokens,
            self.expiration, message_nonce
        ]
        signature = self.obj_utils.generate_signature(data_types=data_types,
                                                      values=values,
                                                      signer_key=signer_key)
        v, r, s = Web3.toInt(
            hexstr="0x" +
            signature[-2:]), signature[:66], "0x" + signature[66:130]
        assert (v == 27 or v == 28)
        assert (len(r) == 66)
        assert (len(s) == 66)
        assert (len(signature) == 132)
        return r, s, v, signature

    def test_create_account(self):
        address, private_key = self.obj_utils.create_account()
        assert (Web3.isAddress(address) == True)
        return address, private_key

    def test_create_transaction_object1(self):
        method_name = "openChannelByThirdParty"
        sender, sender_private_key = self.test_create_account()
        message_nonce = self.obj_utils.get_current_block_no()
        r, s, v, signature = self.generate_signature(
            message_nonce=message_nonce, signer_key=sender_private_key)
        positional_inputs = (sender, SIGNER_ADDRESS, self.recipient,
                             self.group_id, self.agi_tokens, self.expiration,
                             message_nonce, v, r, s)
        transaction_object = self.obj_utils.create_transaction_object(
            *positional_inputs,
            method_name=method_name,
            address=EXECUTOR_ADDRESS,
            contract_path=MPE_CNTRCT_PATH,
            contract_address_path=MPE_ADDR_PATH,
            net_id=self.net_id)
        print(transaction_object)
        raw_transaction = self.obj_utils.sign_transaction_with_private_key(
            transaction_object=transaction_object, private_key=EXECUTOR_KEY)
        transaction_hash = self.obj_utils.process_raw_transaction(
            raw_transaction=raw_transaction)
        print("openChannelByThirdParty::transaction_hash", transaction_hash)

    def test_create_transaction_object2(self):
        method_name = "channelAddFunds"
        positional_inputs = (self.channel_id, self.agi_tokens)
        transaction_object = self.obj_utils.create_transaction_object(
            *positional_inputs,
            method_name=method_name,
            address=EXECUTOR_ADDRESS,
            contract_path=MPE_CNTRCT_PATH,
            contract_address_path=MPE_ADDR_PATH,
            net_id=self.net_id)
        print(transaction_object)
        raw_transaction = self.obj_utils.sign_transaction_with_private_key(
            transaction_object=transaction_object, private_key=EXECUTOR_KEY)

        transaction_hash = self.obj_utils.process_raw_transaction(
            raw_transaction=raw_transaction)
        print("channelAddFunds::transaction_hash", transaction_hash)

    def test_generate_signature_for_state_service(self):
        channel_id = 1
        data_types = ["string", "address", "uint256", "uint256"]
        self.current_block_no = 6487832
        values = [
            "__get_channel_state", self.mpe_address, channel_id,
            self.current_block_no
        ]
        signature = self.obj_utils.generate_signature(data_types=data_types,
                                                      values=values,
                                                      signer_key=SIGNER_KEY)
        v, r, s = Web3.toInt(
            hexstr="0x" +
            signature[-2:]), signature[:66], "0x" + signature[66:130]
        assert (
            signature ==
            "0x0b9bb258a0f975328fd9cd9608bd9b570e7b68cad8d337c940e32b9413e348437dd3614f9c6f776b1eb62d521a5794204a010f581721f167c5b26de0928b139d1c"
        )

    def test_generate_signature_for_daemon_call(self):
        channel_id = 1
        amount = 10
        nonce = 1
        data_types = ["string", "address", "uint256", "uint256", "uint256"]
        values = [
            "__MPE_claim_message", self.mpe_address, channel_id, nonce, amount
        ]
        signature = self.obj_utils.generate_signature(data_types=data_types,
                                                      values=values,
                                                      signer_key=SIGNER_KEY)
        v, r, s = Web3.toInt(
            hexstr="0x" +
            signature[-2:]), signature[:66], "0x" + signature[66:130]
        assert (
            signature ==
            "0x7e50ac20909da29f72ed2ab9cf6c6375f853d8eddfcf3ce33806a4e27b30bcbd5366c41a59647467f0519b0bfc89a50d890b683cd797d5566ba03937f82819c41b"
        )
 def __init__(self, ws_provider):
     self.blockchain_util = BlockChainUtil("WS_PROVIDER", ws_provider)
Ejemplo n.º 26
0
class MPE:
    def __init__(self, obj_repo):
        self.repo = obj_repo
        self.obj_util = Utils()
        self.blockchain_util = BlockChainUtil(
            provider_type="WS_PROVIDER",
            provider=NETWORKS[NETWORK_ID]["ws_provider"])

    def get_channels(self,
                     user_address,
                     org_id=None,
                     service_id=None,
                     group_id=None):
        if user_address and org_id and group_id:
            return self.get_channels_by_user_address_org_group(
                user_address=user_address, org_id=org_id, group_id=group_id)
        elif user_address is not None:
            return self.get_channels_by_user_address(user_address, service_id,
                                                     org_id)

        else:
            raise Exception("Invalid Request")

    def get_channels_by_user_address_v2(self, user_address):
        last_block_no = self.blockchain_util.get_current_block_no()
        logger.info(f"got block number {last_block_no}")
        channel_details_query = "SELECT mc.channel_id, mc.sender, mc.recipient, mc.groupId as group_id, " \
                                "mc.balance_in_cogs, mc.pending, mc.nonce, mc.consumed_balance, mc.expiration, " \
                                "mc.signer, og.org_id, " \
                                "org.organization_name, IF(mc.expiration > %s, 'active','inactive') AS status, " \
                                "og.group_name, org.org_assets_url " \
                                "FROM mpe_channel AS mc JOIN org_group AS og ON mc.groupId = og.group_id " \
                                "JOIN organization AS org ON og.org_id = org.org_id WHERE mc.sender = %s "
        params = [last_block_no, user_address]
        channel_details = self.repo.execute(channel_details_query, params)
        self.obj_util.clean(channel_details)

        channel_details_response = {
            "wallet_address": user_address,
            "organizations":
            self.segregate_org_channel_details(channel_details)
        }

        return channel_details_response

    def segregate_org_channel_details(self, raw_channel_data):
        org_data = {}
        for channel_record in raw_channel_data:
            org_id = channel_record["org_id"]
            group_id = channel_record["group_id"]

            if org_id not in org_data:
                org_data[org_id] = {
                    "org_name":
                    channel_record["organization_name"],
                    "org_id":
                    org_id,
                    "hero_image":
                    json.loads(channel_record["org_assets_url"]).get(
                        "hero_image", ""),
                    "groups": {}
                }
            if group_id not in org_data[org_id]["groups"]:
                org_data[org_id]["groups"][group_id] = {
                    "group_id": group_id,
                    "group_name": channel_record["group_name"],
                    "channels": []
                }

            channel = {
                "channel_id":
                channel_record["channel_id"],
                "recipient":
                channel_record["recipient"],
                'balance_in_cogs':
                channel_record['balance_in_cogs'],
                'consumed_balance':
                channel_record["consumed_balance"],
                'current_balance':
                str(
                    decimal.Decimal(channel_record['balance_in_cogs']) -
                    decimal.Decimal(channel_record["consumed_balance"])),
                "pending":
                channel_record["pending"],
                "nonce":
                channel_record["nonce"],
                "expiration":
                channel_record["expiration"],
                "signer":
                channel_record["signer"],
                "status":
                channel_record["status"]
            }

            org_data[org_id]["groups"][group_id]["channels"].append(channel)

        for org_id in org_data:
            org_data[org_id]["groups"] = list(
                org_data[org_id]["groups"].values())

        return list(org_data.values())

    def get_channels_by_user_address(self, user_address, service_id, org_id):
        last_block_no = self.blockchain_util.get_current_block_no()
        params = [last_block_no]
        print('Inside get_channel_info::user_address', user_address, '|',
              org_id, '|', service_id)
        sub_qry = ""
        if user_address is not None and service_id is not None and org_id is not None:
            sub_qry = " AND S.org_id = %s AND S.service_id = %s "
            params.append(org_id)
            params.append(service_id)
        params.append(user_address)
        raw_channel_dta = self.repo.execute(
            'SELECT  C.*, E.*, IF(C.expiration > %s, "active","inactive") AS status FROM '
            'service_group G, mpe_channel C, service_endpoint E, service S WHERE G.group_id = C.groupId AND '
            'G.group_id = E.group_id and S.row_id = E.service_row_id and E.service_row_id = G.service_row_id '
            'AND S.is_curated = 1  ' + sub_qry +
            ' AND C.sender = %s ORDER BY C.expiration DESC', params)
        self.obj_util.clean(raw_channel_dta)
        channel_dta = {}
        for rec in raw_channel_dta:
            group_id = rec['groupId']
            if group_id not in channel_dta.keys():
                channel_dta[group_id] = {
                    'group_id': group_id,
                    'org_id': rec['org_id'],
                    'service_id': rec['service_id'],
                    'group': {
                        "endpoints": []
                    },
                    'channels': []
                }

            channel = {
                'channel_id':
                rec['channel_id'],
                'recipient':
                rec['recipient'],
                'balance_in_cogs':
                rec['balance_in_cogs'],
                'consumed_balance':
                rec["consumed_balance"],
                'current_balance':
                str(
                    decimal.Decimal(rec['balance_in_cogs']) -
                    decimal.Decimal(rec["consumed_balance"])),
                'pending':
                rec['pending'],
                'nonce':
                rec['nonce'],
                'expiration':
                rec['expiration'],
                'signer':
                rec['signer'],
                'status':
                rec['status']
            }
            endpoint = {
                'endpoint': rec['endpoint'],
                'is_available': rec['endpoint'],
                'last_check_timestamp': rec['last_check_timestamp']
            }
            channel_dta[group_id]['group']['endpoints'].append(endpoint)
            channel_dta[group_id]['channels'].append(channel)
        return list(channel_dta.values())

    def get_channels_by_user_address_org_group(self,
                                               user_address,
                                               org_id=None,
                                               group_id=None):
        last_block_no = self.blockchain_util.get_current_block_no()
        params = [last_block_no, org_id, group_id, user_address]
        raw_channel_data = self.repo.execute(
            "SELECT C.* , OG.payment, OG.org_id, IF(C.expiration > %s, 'active','inactive') AS status FROM "
            "mpe_channel C JOIN org_group OG ON C.groupId = OG.group_id "
            "where OG.org_id = %s and C.groupId = %s and C.sender = %s",
            params)
        self.obj_util.clean(raw_channel_data)
        channel_data = {'group_id': group_id, 'org_id': org_id, 'channels': []}
        for record in raw_channel_data:
            record["payment"] = json.loads(record["payment"])
            if record["recipient"] == record["payment"]["payment_address"]:
                channel = {
                    'channel_id':
                    record['channel_id'],
                    'recipient':
                    record['recipient'],
                    'balance_in_cogs':
                    record['balance_in_cogs'],
                    'consumed_balance':
                    record["consumed_balance"],
                    'current_balance':
                    str(
                        decimal.Decimal(record['balance_in_cogs']) -
                        decimal.Decimal(record["consumed_balance"])),
                    'pending':
                    record['pending'],
                    'nonce':
                    record['nonce'],
                    'expiration':
                    record['expiration'],
                    'signer':
                    record['signer'],
                    'status':
                    record['status']
                }
                channel_data['channels'].append(channel)

        return channel_data

    def get_channel_data_by_group_id_and_channel_id(self, group_id,
                                                    channel_id):
        try:
            result = self.repo.execute(
                "SELECT * FROM mpe_channel WHERE groupId = %s AND channel_id = %s",
                [group_id, channel_id])
            self.obj_util.clean(result)
            return result
        except Exception as e:
            print(repr(e))
            raise e

    def update_consumed_balance(self, channel_id, authorized_amount,
                                full_amount, nonce):
        mpe_repo = MPERepository(self.repo)
        channel = mpe_repo.get_mpe_channels(channel_id)
        if len(channel) != 0 and self.validate_channel_consume_data(
                channel[0], authorized_amount, full_amount, nonce):
            mpe_repo.update_consumed_balance(channel_id, authorized_amount)
        else:
            raise Exception("Channel validation failed")
        return {}

    @staticmethod
    def validate_channel_consume_data(channel_details, authorized_amount,
                                      full_amount, nonce):
        if channel_details["consumed_balance"] < authorized_amount \
                and channel_details["balance_in_cogs"] == full_amount \
                and channel_details["nonce"] == nonce:
            return True
        return False
class WalletService:
    def __init__(self, repo):
        self.repo = repo
        self.boto_utils = BotoUtils(region_name=REGION_NAME)
        self.blockchain_util = BlockChainUtil(
            provider_type="HTTP_PROVIDER",
            provider=NETWORKS[NETWORK_ID]['http_provider'])
        self.utils = Utils()
        self.channel_dao = ChannelDAO(repo=self.repo)
        self.wallet_dao = WalletDAO(repo=self.repo)

    def create_and_register_wallet(self, username):
        address, private_key = self.blockchain_util.create_account()
        wallet = Wallet(address=address,
                        private_key=private_key,
                        type=GENERAL_WALLET_TYPE,
                        status=WalletStatus.ACTIVE.value)
        self._register_wallet(wallet, username)
        return wallet.to_dict()

    def _register_wallet(self, wallet, username):
        existing_wallet = self.wallet_dao.get_wallet_details(wallet)
        if len(existing_wallet) == 0:
            self.wallet_dao.insert_wallet(wallet)
        self.wallet_dao.add_user_for_wallet(wallet, username)

    def register_wallet(self, wallet_address, wallet_type, status, username):
        wallet = Wallet(address=wallet_address,
                        type=wallet_type,
                        status=status)
        self._register_wallet(wallet, username)
        return wallet.to_dict()

    def remove_user_wallet(self, username):
        self.wallet_dao.remove_user_wallet(username)

    def get_wallet_details(self, username):
        """ Method to get wallet details for a given username. """
        logger.info(f"Fetching wallet details for {username}")
        wallet_data = self.wallet_dao.get_wallet_data_by_username(username)
        self.utils.clean(wallet_data)

        logger.info(
            f"Fetched {len(wallet_data)} wallets for username: {username}")
        wallet_response = {"username": username, "wallets": wallet_data}
        return wallet_response

    def __generate_signature_details(self, recipient, group_id, agi_tokens,
                                     expiration, message_nonce, signer_key):
        data_types = [
            "string", "address", "address", "address", "address", "bytes32",
            "uint256", "uint256", "uint256"
        ]
        values = [
            "__openChannelByThirdParty", self.mpe_address,
            self.EXECUTOR_WALLET_ADDRESS, SIGNER_ADDRESS, recipient, group_id,
            agi_tokens, expiration, message_nonce
        ]
        signature = self.blockchain_util.generate_signature(
            data_types=data_types, values=values, signer_key=signer_key)
        v, r, s = Web3.toInt(
            hexstr="0x" +
            signature[-2:]), signature[:66], "0x" + signature[66:130]
        return r, s, v, signature

    def __calculate_amount_in_cogs(self, amount, currency):
        if currency == "USD":
            amount_in_cogs = round(amount)
        else:
            raise Exception("Currency %s not supported.", currency)

        return amount_in_cogs

    def record_create_channel_event(self, payload):
        current_time = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
        if not self.channel_dao.persist_create_channel_event(
                payload, current_time):
            raise Exception("Failed to create record")
        return {}

    def open_channel_by_third_party(self, order_id, sender, signature, r, s, v,
                                    group_id, org_id, amount, currency,
                                    recipient, current_block_no,
                                    amount_in_cogs):
        self.EXECUTOR_WALLET_ADDRESS = self.boto_utils.get_ssm_parameter(
            EXECUTOR_ADDRESS)
        self.EXECUTOR_WALLET_KEY = self.boto_utils.get_ssm_parameter(
            EXECUTOR_KEY)
        method_name = "openChannelByThirdParty"
        self.mpe_address = self.blockchain_util.read_contract_address(
            net_id=NETWORK_ID, path=MPE_ADDR_PATH, key='address')

        # 1 block no is mined in 15 sec on average, setting expiration as 10 years
        expiration = current_block_no + (10 * 365 * 24 * 60 * 4)
        # amount_in_cogs = self.__calculate_amount_in_cogs(amount=amount, currency=currency)
        self.__validate__cogs(amount_in_cogs=amount_in_cogs)

        group_id_in_hex = "0x" + base64.b64decode(group_id).hex()

        positional_inputs = (sender, SIGNER_ADDRESS, recipient,
                             group_id_in_hex, amount_in_cogs, expiration,
                             current_block_no, v, r, s)

        transaction_object = self.blockchain_util.create_transaction_object(
            *positional_inputs,
            method_name=method_name,
            address=self.EXECUTOR_WALLET_ADDRESS,
            contract_path=MPE_CNTRCT_PATH,
            contract_address_path=MPE_ADDR_PATH,
            net_id=NETWORK_ID)

        raw_transaction = self.blockchain_util.sign_transaction_with_private_key(
            transaction_object=transaction_object,
            private_key=self.EXECUTOR_WALLET_KEY)
        transaction_hash = self.blockchain_util.process_raw_transaction(
            raw_transaction=raw_transaction)

        logger.info(
            "openChannelByThirdParty::transaction_hash : %s for order_id : %s",
            transaction_hash, order_id)

        self.channel_dao.insert_channel_history(
            order_id=order_id,
            amount=amount,
            currency=currency,
            group_id=group_id,
            org_id=org_id,
            type=method_name,
            recipient=recipient,
            address=sender,
            signature=signature,
            request_parameters=str(positional_inputs),
            transaction_hash=transaction_hash,
            status=TransactionStatus.PENDING)

        return {
            "transaction_hash": transaction_hash,
            "signature": signature,
            "amount_in_cogs": amount_in_cogs,
            "type": method_name
        }

    def set_default_wallet(self, username, address):
        self.wallet_dao.set_default_wallet(username=username, address=address)
        return "OK"

    def add_funds_to_channel(self, org_id, group_id, channel_id, sender,
                             recipient, order_id, amount, currency,
                             amount_in_cogs):
        self.EXECUTOR_WALLET_ADDRESS = self.boto_utils.get_ssm_parameter(
            EXECUTOR_ADDRESS)
        self.EXECUTOR_WALLET_KEY = self.boto_utils.get_ssm_parameter(
            EXECUTOR_KEY)
        method_name = "channelAddFunds"
        # amount_in_cogs = self.__calculate_amount_in_cogs(amount=amount, currency=currency)
        self.__validate__cogs(amount_in_cogs=amount_in_cogs)
        positional_inputs = (channel_id, amount_in_cogs)

        transaction_object = self.blockchain_util.create_transaction_object(
            *positional_inputs,
            method_name=method_name,
            address=self.EXECUTOR_WALLET_ADDRESS,
            contract_path=MPE_CNTRCT_PATH,
            contract_address_path=MPE_ADDR_PATH,
            net_id=NETWORK_ID)

        raw_transaction = self.blockchain_util.sign_transaction_with_private_key(
            transaction_object=transaction_object,
            private_key=self.EXECUTOR_WALLET_KEY)

        transaction_hash = self.blockchain_util.process_raw_transaction(
            raw_transaction=raw_transaction)
        logger.info("channelAddFunds::transaction_hash: %s for order_id: %s",
                    transaction_hash, order_id)

        self.channel_dao.insert_channel_history(
            order_id=order_id,
            amount=amount,
            currency=currency,
            group_id=group_id,
            org_id=org_id,
            type=method_name,
            recipient=recipient,
            address=sender,
            signature=None,
            request_parameters=str(positional_inputs),
            transaction_hash=transaction_hash,
            status=TransactionStatus.PENDING)
        return {
            "transaction_hash": transaction_hash,
            "amount_in_cogs": amount_in_cogs,
            "type": method_name
        }

    def get_transactions_from_username_recipient(self, username, org_id,
                                                 group_id):
        logger.info(
            f"Fetching transactions for {username} to org_id: {org_id} group_id: {org_id}"
        )
        channel_data = self.channel_dao.get_channel_transactions_for_username_recipient(
            username=username, group_id=group_id, org_id=org_id)
        self.utils.clean(channel_data)

        logger.info(f"Fetched {len(channel_data)} transactions")
        transaction_details = {"username": username, "wallets": []}

        wallet_transactions = dict()
        for rec in channel_data:
            sender_address = rec["address"]
            if rec["address"] not in wallet_transactions:
                wallet_transactions[rec["address"]] = {
                    "address": sender_address,
                    "is_default": rec["is_default"],
                    "type": rec["type"],
                    "transactions": []
                }
            if rec['recipient'] is None:
                continue

            transaction = {
                "org_id": org_id,
                "group_id": group_id,
                "recipient": rec["recipient"],
                "amount": rec["amount"],
                "transaction_type": rec["transaction_type"],
                "currency": rec["currency"],
                "status": rec["status"],
                "created_at": rec["created_at"],
            }

            wallet_transactions[sender_address]["transactions"].append(
                transaction)

        for key in wallet_transactions:
            wallet = wallet_transactions[key]
            transaction_details["wallets"].append(wallet)
        return transaction_details

    def get_channel_transactions_against_order_id(self, order_id):
        transaction_history = self.channel_dao.get_channel_transactions_against_order_id(
            order_id)

        for record in transaction_history:
            record["created_at"] = record["created_at"].strftime(
                "%Y-%m-%d %H:%M:%S")

        return {"order_id": order_id, "transactions": transaction_history}

    def __validate__cogs(self, amount_in_cogs):
        if amount_in_cogs < MINIMUM_AMOUNT_IN_COGS_ALLOWED:
            raise Exception(
                "Insufficient amount to buy minimum amount in cogs allowed.")
Ejemplo n.º 28
0
class RegistryBlockChainUtil:
    def __init__(self, env_type):
        self.__env_type = env_type
        if env_type == EnvironmentType.MAIN.value:
            self.__network_id = NETWORK_ID
            self.__contract_path = REG_CNTRCT_PATH
            self.__executor_address = ""
            self.__contract_address_path = REG_ADDR_PATH
            self.__blockchain_util = BlockChainUtil(
                provider_type="HTTP_PROVIDER",
                provider=NETWORKS[self.__network_id]['http_provider'])

        elif env_type == EnvironmentType.TEST.value:
            self.__contract_path = TEST_REG_CNTRCT_PATH
            self.__contract_address_path = TEST_REG_ADDR_PATH
            self.__executor_address = BLOCKCHAIN_TEST_ENV["publisher_address"]
            self.__network_id = BLOCKCHAIN_TEST_ENV["network_id"]
            self.__blockchain_util = BlockChainUtil(
                provider_type="HTTP_PROVIDER",
                provider=BLOCKCHAIN_TEST_ENV['http_provider'])
        else:
            raise MethodNotImplemented()

    def is_org_published(self, org_id):
        contract = self.__blockchain_util.load_contract(
            path=self.__contract_path)
        contract_address = self.__blockchain_util.read_contract_address(
            net_id=self.__network_id,
            path=self.__contract_address_path,
            key='address')
        return self.__organization_exist_in_blockchain(org_id, contract,
                                                       contract_address)

    def __organization_exist_in_blockchain(self, org_id, contract,
                                           contract_address):
        method_name = "getOrganizationById"
        positional_inputs = (web3.Web3.toHex(text=org_id), )
        contract = self.__blockchain_util.contract_instance(
            contract_abi=contract, address=contract_address)

        org_data = self.__blockchain_util.call_contract_function(
            contract=contract,
            contract_function=method_name,
            positional_inputs=positional_inputs)

        logger.info(f"Org data :: {org_data}")
        org_found = org_data[0]
        return org_found

    def __generate_blockchain_transaction_for_test_environment(
            self, *positional_inputs, method_name):
        transaction_object = self.__blockchain_util.create_transaction_object(
            *positional_inputs,
            method_name=method_name,
            address=self.__executor_address,
            contract_path=self.__contract_path,
            contract_address_path=self.__contract_address_path,
            net_id=self.__network_id)
        return transaction_object

    def __make_trasaction(self, *positional_inputs, method_name):
        if self.__env_type == EnvironmentType.TEST.value:
            executor_key = BotoUtils(REGION_NAME).get_ssm_parameter(
                BLOCKCHAIN_TEST_ENV["publisher_private_key"])
            transaction_object = self.__generate_blockchain_transaction_for_test_environment(
                *positional_inputs, method_name=method_name)
        else:
            raise EnvironmentNotFoundException()
        raw_transaction = self.__blockchain_util.sign_transaction_with_private_key(
            transaction_object=transaction_object, private_key=executor_key)
        transaction_hash = self.__blockchain_util.process_raw_transaction(
            raw_transaction=raw_transaction)
        return transaction_hash

    def __update_organization_in_blockchain(self, org_id, metadata_uri):
        method_name = "changeOrganizationMetadataURI"
        positional_inputs = (web3.Web3.toHex(text=org_id),
                             ipfsuri_to_bytesuri(metadata_uri))
        transaction_hash = self.__make_trasaction(*positional_inputs,
                                                  method_name=method_name)
        logger.info(
            f"transaction hash {transaction_hash} generated while registering organization "
            f"{org_id} in {self.__env_type} blockchain environment.")
        return transaction_hash

    def __register_organization_in_blockchain(self, org_id, metadata_uri,
                                              members):
        method_name = "createOrganization"
        positional_inputs = (web3.Web3.toHex(text=org_id),
                             ipfsuri_to_bytesuri(metadata_uri), members)
        transaction_hash = self.__make_trasaction(*positional_inputs,
                                                  method_name=method_name)
        logger.info(
            f"transaction hash {transaction_hash} generated while registering organization "
            f"{org_id} in {self.__env_type} blockchain environment.")
        return transaction_hash

    def publish_organization_to_test_network(self, organization):
        metadata_uri = organization.metadata_ipfs_uri
        members = []
        org_id = organization.id
        if self.__env_type == EnvironmentType.TEST.value:
            if self.is_org_published(org_id):
                return self.__update_organization_in_blockchain(
                    org_id, metadata_uri)
            else:
                return self.__register_organization_in_blockchain(
                    org_id, metadata_uri, members)
        else:
            raise EnvironmentNotFoundException()

    def __service_exist_in_blockchain(self, org_id, service_id, contract,
                                      contract_address):
        method_name = "getServiceRegistrationById"
        positional_inputs = (web3.Web3.toHex(text=org_id),
                             web3.Web3.toHex(text=service_id))
        contract = self.__blockchain_util.contract_instance(
            contract_abi=contract, address=contract_address)
        service_data = self.__blockchain_util.call_contract_function(
            contract=contract,
            contract_function=method_name,
            positional_inputs=positional_inputs)
        logger.info(f"Services :: {service_data}")
        service_found = service_data[0]
        return service_found

    def update_service_in_blockchain(self, org_id, service_id, metadata_uri):
        method_name = "updateServiceRegistration"
        positional_inputs = (web3.Web3.toHex(text=org_id),
                             web3.Web3.toHex(text=service_id),
                             ipfsuri_to_bytesuri(metadata_uri))
        transaction_hash = self.__make_trasaction(*positional_inputs,
                                                  method_name=method_name)
        logger.info(
            f"transaction hash {transaction_hash} generated while "
            f"updating service {service_id} in {self.__env_type} blockchain environment."
        )
        return transaction_hash

    def register_service_in_blockchain(self, org_id, service_id, metadata_uri,
                                       tags):
        method_name = "createServiceRegistration"
        positional_inputs = (web3.Web3.toHex(text=org_id),
                             web3.Web3.toHex(text=service_id),
                             ipfsuri_to_bytesuri(metadata_uri),
                             [web3.Web3.toHex(text=tag) for tag in tags])
        transaction_hash = self.__make_trasaction(*positional_inputs,
                                                  method_name=method_name)
        logger.info(
            f"transaction hash {transaction_hash} generated while registering service {service_id} "
            f"in {self.__env_type} blockchain environment.")
        return transaction_hash

    def is_service_published(self, org_id, service_id):
        contract = self.__blockchain_util.load_contract(
            path=self.__contract_path)
        contract_address = self.__blockchain_util.read_contract_address(
            net_id=self.__network_id,
            path=self.__contract_address_path,
            key='address')
        return self.__service_exist_in_blockchain(org_id, service_id, contract,
                                                  contract_address)

    def register_or_update_service_in_blockchain(self, org_id, service_id,
                                                 metadata_uri, tags):
        if not self.is_org_published(org_id=org_id):
            raise OrganizationNotFoundException()

        if self.is_service_published(org_id=org_id, service_id=service_id):
            transaction_hash = self.update_service_in_blockchain(
                org_id, service_id, metadata_uri)
        else:
            transaction_hash = self.register_service_in_blockchain(
                org_id, service_id, metadata_uri, tags)
        return transaction_hash
Ejemplo n.º 29
0
from common.blockchain_util import BlockChainUtil
from common.utils import Utils
from rfai.config import NETWORK
from rfai.dao.foundation_member_data_access_object import FoundationMemberDAO
from rfai.dao.request_data_access_object import RequestDAO
from rfai.dao.solution_data_access_object import SolutionDAO
from rfai.dao.stake_data_access_object import StakeDAO
from rfai.dao.vote_data_access_object import VoteDAO
from rfai.dao.rfai_request_repository import RFAIRequestRepository
from rfai.rfai_status import RFAIStatusCodes
import json
from common.logger import get_logger

obj_utils = Utils()
obj_blockchain_utils = BlockChainUtil(provider_type="HTTP_PROVIDER",
                                      provider=NETWORK["http_provider"])
logger = get_logger(__name__)


class RFAIService:
    def __init__(self, repo):
        self.request_dao = RequestDAO(repo=repo)
        self.vote_dao = VoteDAO(repo=repo)
        self.solution_dao = SolutionDAO(repo=repo)
        self.stake_dao = StakeDAO(repo=repo)
        self.foundation_member_dao = FoundationMemberDAO(repo=repo)
        self.rfai_request_dao = RFAIRequestRepository(repo=repo)

    def _format_filter_params(self, query_parameters):
        filter_params = {}
        # if "requester" in query_parameters.keys():
Ejemplo n.º 30
0
 def __init__(self, net_id, ws_provider, ipfs_url, ipfs_port):
     self._ipfs_util = IPFSUtil(ipfs_url, ipfs_port)
     self._blockchain_util = BlockChainUtil("WS_PROVIDER", ws_provider)
     self._net_id = net_id