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 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.")
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 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
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 }
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']) 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 __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 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): 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) return transaction_hash