class WalletService:
    def __init__(self, repo):
        self.repo = repo
        self.boto_utils = BotoUtils(region_name=REGION_NAME)
        self.blockchain_util = BlockChainUtil(
            provider_type="HTTP_PROVIDER",
            provider=NETWORKS[NETWORK_ID]['http_provider'])
        self.utils = Utils()
        self.channel_dao = ChannelDAO(repo=self.repo)
        self.wallet_dao = WalletDAO(repo=self.repo)

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

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

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

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

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

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

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

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

        return amount_in_cogs

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def __validate__cogs(self, amount_in_cogs):
        if amount_in_cogs < MINIMUM_AMOUNT_IN_COGS_ALLOWED:
            raise Exception(
                "Insufficient amount to buy minimum amount in cogs allowed.")
Ejemplo n.º 2
0
class TestUtils(unittest.TestCase):
    def setUp(self):
        self.net_id = 3
        self.http_provider = Web3.HTTPProvider(
            NETWORKS[self.net_id]['http_provider'])
        self.obj_utils = BlockChainUtil(provider_type="HTTP_PROVIDER",
                                        provider=self.http_provider)
        self.mpe_address = self.obj_utils.read_contract_address(
            net_id=self.net_id, path=MPE_ADDR_PATH, key='address')
        self.recipient = "0x9c302750c50307D3Ad88eaA9a6506874a15cE4Cb"
        self.group_id = "0x" + base64.b64decode(
            "DS2OoKSfGE/7hAO/023psl4Qdj0MJvYsreJbCoAHVSc=").hex()
        self.agi_tokens = 1
        self.current_block_no = self.obj_utils.get_current_block_no()
        self.expiration = self.current_block_no + 10000000
        self.channel_id = 0

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

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

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

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

        transaction_hash = self.obj_utils.process_raw_transaction(
            raw_transaction=raw_transaction)
        print("channelAddFunds::transaction_hash", transaction_hash)
class 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, 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
        }