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
class BlockchainEventProducer(EventProducer): def __init__(self, http_provider, repository=None, ): self._blockchain_util = BlockChainUtil("HTTP_PROVIDER", http_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() logger.info(f"Current block number={current_block_number}") if READ_EVENTS_WITH_BLOCK_DIFFERENCE: block_number_with_difference = current_block_number - READ_EVENTS_WITH_BLOCK_DIFFERENCE else: block_number_with_difference = current_block_number end_block_number = last_processed_block_number + batch_limit if block_number_with_difference <= end_block_number: end_block_number = block_number_with_difference return end_block_number def produce_event(self, net_id): pass
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" )
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
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 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 }
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 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 }