示例#1
0
def test_decode_addr_success():
    # test enocded_addr_with_none_subaddr
    addr, subaddr = identifier.decode_account(enocded_addr_with_none_subaddr,
                                              "lbr")
    assert addr.to_hex() == test_onchain_address
    assert subaddr is None

    # test enocded_addr_with_subaddr
    addr, subaddr = identifier.decode_account(enocded_addr_with_subaddr, "lbr")
    assert addr.to_hex() == test_onchain_address
    assert subaddr.hex() == test_sub_address
示例#2
0
def test_decode_addr_success(hrp_addresses):
    hrp, enocded_addr_with_none_subaddr, enocded_addr_with_subaddr = hrp_addresses
    # test enocded_addr_with_none_subaddr
    addr, subaddr = identifier.decode_account(enocded_addr_with_none_subaddr, hrp)
    assert addr.to_hex() == test_onchain_address
    assert subaddr is None

    # test enocded_addr_with_subaddr
    addr, subaddr = identifier.decode_account(enocded_addr_with_subaddr, hrp)
    assert addr.to_hex() == test_onchain_address
    assert subaddr.hex() == test_sub_address
async def test_send_payment_with_invalid_account_identifier_hrp_as_payee(
        stub_client: RestClient, target_client: RestClient, currency: str,
        hrp: str) -> None:
    """
    Test Plan:

    1. Generate valid receive payment account identifier.
    2. Extract account onchain address and subaddress from receiving payment account identifier.
    3. Use a different hrp and extracted account address and subaddress to create a new account identifier.
    4. Call send payment `POST /accounts/{account_id}/payments` with created account identifier.
    5. Expect server response 400 client error, and sender account balance is not changed.
    """

    amount = 1_000_000
    sender_account = await target_client.create_account(
        balances={currency: amount})
    receiver_account = await stub_client.create_account()
    try:
        receiver_account_identifier = await receiver_account.generate_account_identifier(
        )
        account_address, subaddress = identifier.decode_account(
            receiver_account_identifier, hrp)
        new_hrp = identifier.TDM if hrp != identifier.TDM else identifier.PDM
        new_account_identifier = identifier.encode_account(
            account_address, subaddress, new_hrp)
        with pytest.raises(aiohttp.ClientResponseError, match="400"):
            await sender_account.send_payment(currency=currency,
                                              amount=amount,
                                              payee=new_account_identifier)
        assert await sender_account.balance(currency) == amount
    finally:
        await receiver_account.log_events()
        await sender_account.log_events()
示例#4
0
async def send_request_json(
    diem_client: AsyncClient,
    sender_account: LocalAccount,
    sender_address: Optional[str],
    receiver_address: str,
    request_json: str,
    hrp: str,
    x_request_id: Optional[str] = str(uuid.uuid4()),
    request_body: Optional[bytes] = None,
) -> Tuple[int, offchain.CommandResponseObject]:
    headers = {}
    if x_request_id:
        headers[offchain.http_header.X_REQUEST_ID] = x_request_id
    if sender_address:
        headers[offchain.http_header.X_REQUEST_SENDER_ADDRESS] = sender_address

    account_address, _ = identifier.decode_account(receiver_address, hrp)
    base_url, public_key = await diem_client.get_base_url_and_compliance_key(
        account_address)
    if request_body is None:
        request_body = jws.encode(request_json,
                                  sender_account.compliance_key.sign)

    url = f"{base_url.rstrip('/')}/v2/command"
    async with diem_client._session.post(url,
                                         data=request_body,
                                         headers=headers) as resp:
        cmd_resp_obj = offchain.jws.deserialize(await resp.read(),
                                                offchain.CommandResponseObject,
                                                public_key.verify)
        return (resp.status, cmd_resp_obj)
示例#5
0
async def target_account_vasp_domains(target_client: RestClient,
                                      diem_client: AsyncClient,
                                      hrp: str) -> List[str]:
    account_identifier = await target_client.random_account_identifier()
    account_address, _ = identifier.decode_account(account_identifier, hrp)
    account = await diem_client.get_parent_vasp_account(account_address)
    return list(account.role.vasp_domains)
示例#6
0
        def post(self):
            try:
                tx_params = request.json

                user = self.user
                account_id = user.account_id

                currency = DiemCurrency[tx_params["currency"]]
                amount = int(tx_params["amount"])
                recv_address: str = tx_params["receiver_address"]
                dest_address, dest_subaddress = identifier.decode_account(
                    recv_address,
                    context.get().config.diem_address_hrp())

                tx = transaction_service.send_transaction(
                    sender_id=account_id,
                    amount=amount,
                    currency=currency,
                    destination_address=utils.account_address_bytes(
                        dest_address).hex(),
                    destination_subaddress=dest_subaddress.hex()
                    if dest_subaddress else None,
                )
                transaction = AccountRoutes.get_transaction_response_object(
                    user.account_id, tx)
                return transaction, HTTPStatus.OK
            except transaction_service.RiskCheckError as risk_check_failed_error:
                return self.respond_with_error(HTTPStatus.FAILED_DEPENDENCY,
                                               str(risk_check_failed_error))
            except transaction_service.SelfAsDestinationError as send_to_self_error:
                return self.respond_with_error(HTTPStatus.FORBIDDEN,
                                               str(send_to_self_error))
def test_send_payment_with_invalid_account_identifier_onchain_account_address_as_payee(
        stub_client: RestClient, target_client: RestClient, currency: str,
        hrp: str) -> None:
    """
    Test Plan:

    1. Generate valid receive payment account identifier.
    2. Extract account onchain address and subaddress from receiving payment account identifier.
    3. Use an invalid onchain account address and extracted subaddress to create a new account identifier.
    4. Call send payment `POST /accounts/{account_id}/payments` with created account identifier.
    5. Expect server response 400 client error, and sender account balance is not changed.
    """

    amount = 1_000_000
    sender_account = target_client.create_account(balances={currency: amount})
    receiver_account = stub_client.create_account()
    try:
        receiver_account_identifier = receiver_account.generate_account_identifier(
        )
        _, subaddress = identifier.decode_account(receiver_account_identifier,
                                                  hrp)
        invalid_account_address = LocalAccount().account_address
        invalid_account_identifier = identifier.encode_account(
            invalid_account_address, subaddress, hrp)
        with pytest.raises(requests.HTTPError, match="400 Client Error"):
            sender_account.send_payment(currency=currency,
                                        amount=amount,
                                        payee=invalid_account_identifier)
        assert sender_account.balance(currency) == amount
    finally:
        receiver_account.log_events()
        sender_account.log_events()
def test_send_payment_with_invalid_account_identifier_hrp_as_payee(
        sender_account: AccountResource, receiver_account: AccountResource,
        currency: str, hrp: str) -> None:
    """
    Test Plan:

    1. Generate valid receive payment account identifier.
    2. Extract account onchain address and subaddress from receiving payment account identifier.
    3. Use a different hrp and extracted account address and subaddress to create a new account identifier.
    4. Call send payment `POST /accounts/{account_id}/payments` with created account identifier.
    5. Expect server response 400 client error, and sender account balance is not changed.
    """

    initial_amount = sender_account.balance(currency)
    receiver_account_identifier = receiver_account.generate_account_identifier(
    )
    account_address, subaddress = identifier.decode_account(
        receiver_account_identifier, hrp)
    new_hrp = identifier.TDM if hrp != identifier.TDM else identifier.PDM
    new_account_identifier = identifier.encode_account(account_address,
                                                       subaddress, new_hrp)
    with pytest.raises(requests.HTTPError, match="400 Client Error"):
        sender_account.send_payment(currency=currency,
                                    amount=amount,
                                    payee=new_account_identifier)
    assert sender_account.balance(currency) == initial_amount
示例#9
0
 def _send_additional_kyc_data(self, command: offchain.Command) -> typing.Tuple[ActionResultType, offchain.Command]:
     command = typing.cast(offchain.PaymentCommand, command)
     account_id = command.my_actor_obj().address
     _, subaddress = identifier.decode_account(account_id, self.hrp)
     if subaddress:
         user = self._find_user_by_subaddress(subaddress)
     else:
         raise ValueError(f"{account_id} has no sub-address")
     new_cmd = command.new_command(additional_kyc_data=user.additional_kyc_data())
     return (ActionResult.SENT_ADDITIONAL_KYC_DATA, new_cmd)
示例#10
0
    def __trade_and_execute_buy(self, quote, trade, diem_deposit_address):
        if not diem_deposit_address:
            raise TradeError("Can't execute trade without a deposit address")

        receiver_vasp, receiver_subaddress = identifier.decode_account(
            diem_deposit_address, identifier.HRPS[CHAIN_ID.to_int()])

        tx_version, tx_sequence = self.send_transaction(
            currency=quote.currency_pair.value.base,
            amount=quote.amount,
            dest_vasp_address=utils.account_address_hex(receiver_vasp),
            dest_sub_address=receiver_subaddress.hex(),
        )
        trade.executed(tx_version)
示例#11
0
    def _trade_and_execute_buy(self, quote, trade, diem_deposit_address):
        if not diem_deposit_address:
            raise TradeError("Can't execute trade without a deposit address")

        receiver_vasp, receiver_sub_address = identifier.decode_account(
            diem_deposit_address, identifier.HRPS[testnet.CHAIN_ID.to_int()])

        client = testnet.create_client()

        account = client.get_account(receiver_vasp)

        faucet = testnet.Faucet(client)
        faucet.mint(account.authentication_key, quote.amount,
                    quote.currency_pair.value.base)
        trade.executed(-1)
def refund(payment):
    if not payment_can_refund(payment):
        raise InvalidPaymentStatus("unclearedrefund")

    if not len(payment.chain_transactions) == 1:
        raise InvalidPaymentStatus("chain_transactions")
    # TODO - support refund of multiple payments

    # TODO - Resolve refund address, currency and amount from blockchain

    target_transaction = payment.chain_transactions[0]
    if target_transaction.is_refund:
        raise InvalidPaymentStatus("refund_transaction")

    refund_target_address, refund_target_sub_address = identifier.decode_account(
        target_transaction.sender_address, CHAIN_HRP)
    refund_target_address = utils.account_address_hex(refund_target_address)
    refund_target_sub_address = refund_target_sub_address.hex()

    refund_amount = target_transaction.amount  # payment.requested_amount
    refund_currency = DiemCurrency(target_transaction.currency)

    payment.set_status(PaymentStatus.refund_requested)

    refund_tx_id = None

    try:
        wallet = OnchainWallet()

        refund_tx_id, _ = wallet.send_transaction(
            refund_currency,
            refund_amount,
            refund_target_address,
            refund_target_sub_address,  # TODO - new sub_address for refund?
        )
        payment.add_chain_transaction(
            amount=refund_amount,
            sender_address=wallet.address_str,
            currency=refund_currency,
            tx_id=refund_tx_id,
            is_refund=True,
        )
        payment.set_status(PaymentStatus.refund_completed)
    except Exception as e:  # TODO - narrow to blockchain exception
        logger.exception("Failed during refund")
        payment.set_status(PaymentStatus.refund_error)

    return refund_tx_id, target_transaction
    def mint(
        self,
        authkey_hex: str,
        amount: int,
        identifier: str,
        session: Optional[requests.Session] = None,
        timeout: Optional[Union[float, Tuple[float, float]]] = None,
    ) -> int:
        dd_address_hex = account_address_hex(DESIGNATED_DEALER_ADDRESS)
        account = BlockchainMock.get_account_resource(dd_address_hex)
        sequence = account.sequence_number
        version = BlockchainMock.blockchain.version

        decoded_addr, decoded_subaddr = decode_account(authkey_hex)

        metadata = general_metadata(to_subaddress=decoded_subaddr)

        address_hex_bytes = account_address_bytes(decoded_addr)

        process_incoming_transaction(
            sender_address=dd_address_hex,
            receiver_address=authkey_hex,
            sequence=sequence,
            amount=amount,
            currency=DiemCurrency.Coin1,
            metadata=metadata,
        )

        BlockchainMock.blockchain.version += 1

        tx = MockSignedTransaction(
            sender=bytes.fromhex(ASSOC_AUTHKEY),
            amount=amount,
            currency=identifier,
            receiver=address_hex_bytes,
            metadata=metadata,
            sequence=account.sequence_number,
            version=version,
        )
        account.sequence_number += 1
        account.transactions[sequence] = tx
        BlockchainMock.blockchain.transactions[version] = tx

        return sequence
示例#14
0
def _cover_buy(order: Order, quote: QuoteData) -> bool:
    deposit_address = get_inventory_deposit_address()
    trade_id = LpClient().trade_and_execute(
        quote_id=quote.quote_id,
        direction=Direction[order.direction],
        diem_deposit_address=deposit_address,
    )
    trade_info = _wait_for_trade(order=order, trade_id=trade_id)

    if not trade_info:
        update_order(
            order_id=OrderId(UUID(order.id)),
            cover_status=CoverStatus.FailedCoverLPTradeError,
        )
        return False

    update_order(
        order_id=OrderId(UUID(order.id)),
        cover_status=CoverStatus.PendingCoverValidation,
    )

    vasp_address, internal_subaddress = decode_account(
        deposit_address, context.get().config.diem_address_hrp()
    )
    if not _validate_blockchain_transaction(
        blockchain_version=trade_info.tx_version,
        vasp_address=utils.account_address_hex(vasp_address),
        internal_subaddress=internal_subaddress.hex(),
        amount=round(trade_info.amount),
    ):
        update_order(
            order_id=OrderId(UUID(order.id)),
            cover_status=CoverStatus.FailedCoverTransactionError,
        )
        return False

    return True
示例#15
0
    def trade_and_execute(
        self,
        quote_id: QuoteId,
        direction: Direction,
        diem_deposit_address: Optional[str] = None,
        tx_version: Optional[int] = None,
    ) -> TradeId:
        quote = LpClientMock.QUOTES[quote_id]
        trade_id = TradeId(uuid4())
        metadata = diem_types.Metadata__Undefined()
        if diem_deposit_address is not None:
            addr, subaddr = identifier.decode_account(diem_deposit_address,
                                                      "tdm")
            metadata = general_metadata(to_subaddress=subaddr)
        if direction == Direction.Buy:
            process_incoming_transaction(
                sender_address="",
                receiver_address=diem_deposit_address,
                sequence=1,
                amount=quote.amount,
                currency=quote.rate.pair.base.value,
                metadata=metadata,
                blockchain_version=1,
            )

        trade_data = TradeData(
            trade_id=trade_id,
            direction=direction,
            pair=quote.rate.pair,
            amount=quote.amount,
            status=TradeStatus.Complete,
            quote=quote,
            tx_version=1,
        )
        LpClientMock.TRADES[trade_id] = trade_data
        return trade_id
def test_sender_addresses(factory):
    cmd = factory.new_sender_payment_command()
    account_address, subaddress = identifier.decode_account(
        cmd.payment.sender.address, factory.hrp())
    assert account_address == cmd.sender_account_address(factory.hrp())
    assert subaddress == cmd.sender_subaddress(factory.hrp())
示例#17
0
def _account_address_and_subaddress(
        account_id: str) -> Tuple[str, Optional[str]]:
    account_address, sub = identifier.decode_account(
        account_id,
        context.get().config.diem_address_hrp())
    return (account_address.to_hex(), sub.hex() if sub else None)
示例#18
0
def test_decode_addr_fail():
    # fail to decode invalid hrp
    invalid_hrp_encoded_address = "btc1p7ujcndcl7nudzwt8fglhx6wxn08kgs5tm6mz4usw5p72t"
    with pytest.raises(ValueError):
        identifier.decode_account(invalid_hrp_encoded_address, "lbr")

    # fail to decode invalid "expected" hrp
    with pytest.raises(ValueError):
        identifier.decode_account(
            "lbr1p7ujcndcl7nudzwt8fglhx6wxn08kgs5tm6mz4usw5p72t", "tlb")

    # fail to decode invalid version
    invalid_version_encoded_address = "lbr1q7ujcndcl7nudzwt8fglhx6wxn08kgs5tm6mz4usw5p72t"  # v = 0
    with pytest.raises(ValueError):
        identifier.decode_account(invalid_version_encoded_address, "lbr")

    # fail to decode due to checksum error
    invalid_checksum_encoded_address = (
        "lbr1p7ujcndcl7nudzwt8fglhx6wxn08kgs5tm6mz4usw5p72p"  # change last char from t to p
    )
    with pytest.raises(ValueError):
        identifier.decode_account(invalid_checksum_encoded_address, "lbr")

    # fail to decode mixed case per BIP 173
    mixedcase_encoded_address = "LbR1p7ujcndcl7nudzwt8fglhx6wxn08kgs5tm6mz4usw5P72T"  # some uppercase
    with pytest.raises(ValueError):
        identifier.decode_account(mixedcase_encoded_address, "lbr")

    # fail to decode shorter payload
    short_encoded_address = "lbr1p7ujcndcl7nudzwt8fglhx6wxnvqqqqqqqqqqqqelu3xv"  # sample 23 bytes encoded
    with pytest.raises(ValueError):
        identifier.decode_account(short_encoded_address, "lbr")

    # fail to decode larger payload
    large_encoded_address = "lbr1p7ujcndcl7nudzwt8fglhx6wxn08kgs5tm6mz4us4g3ysw8a"  # sample 25 bytes encoded
    with pytest.raises(ValueError):
        identifier.decode_account(large_encoded_address, "lbr")

    # fail to decode invalid separator
    invalid_separator_encoded_address = "lbr2p7ujcndcl7nudzwt8fglhx6wxn08kgs5tm6mz4usw5p72t"  # separator = 2
    with pytest.raises(ValueError):
        identifier.decode_account(invalid_separator_encoded_address, "lbr")

    # fail to decode invalid character
    invalid_char_encoded_address = "lbr1pbujcndcl7nudzwt8fglhx6wxn08kgs5tm6mz4usw5p72t"  # add b char
    with pytest.raises(ValueError):
        identifier.decode_account(invalid_char_encoded_address, "lbr")
示例#19
0
 def general_metadata(self, from_subaddress: bytes,
                      payee: str) -> Tuple[bytes, bytes]:
     to_account, to_subaddress = identifier.decode_account(payee, self.hrp)
     return (txnmetadata.general_metadata(from_subaddress,
                                          to_subaddress), b"")
示例#20
0
 def decode_account_identifier(
         self, encoded_id: str
 ) -> Tuple[diem_types.AccountAddress, Optional[bytes]]:
     return identifier.decode_account(encoded_id, self.hrp)
示例#21
0
def test_encode_decode_with_random_hrp():
    # test with none sub_address
    id = identifier.encode_account(test_onchain_address, None, "abc")
    addr, sub = identifier.decode_account(id, "abc")
    assert addr.to_hex() == test_onchain_address
    assert sub is None
示例#22
0
def test_decode_addr_fail(hrp_addresses):
    hrp, enocded_addr_with_none_subaddr, enocded_addr_with_subaddr = hrp_addresses

    # fail to decode invalid hrp
    invalid_hrp_encoded_address = "btc1p7ujcndcl7nudzwt8fglhx6wxn08kgs5tm6mz4usw5p72t"
    with pytest.raises(ValueError):
        identifier.decode_account(invalid_hrp_encoded_address, hrp)

    # fail to decode invalid "expected" hrp
    with pytest.raises(ValueError):
        identifier.decode_account(enocded_addr_with_none_subaddr, "xdm")

    # fail to decode invalid version
    invalid_version_encoded_address = enocded_addr_with_none_subaddr.replace(
        "1p7", "1q7")  # p (1) -> q (2)
    with pytest.raises(ValueError):
        identifier.decode_account(invalid_version_encoded_address, hrp)

    # fail to decode due to checksum error
    invalid_checksum_encoded_address = enocded_addr_with_none_subaddr.replace(
        "d8p9cq", "d8p9c7").replace("v88j4s", "v88j4q")
    with pytest.raises(ValueError):
        identifier.decode_account(invalid_checksum_encoded_address, hrp)

    # fail to decode mixed case per BIP 173
    mixedcase_encoded_address = enocded_addr_with_none_subaddr.replace(
        "qqqqqqqqqqqqq", "qqQqqqqqqqqqq")
    with pytest.raises(ValueError):
        identifier.decode_account(mixedcase_encoded_address, hrp)

    # fail to decode shorter payload
    short_encoded_address = enocded_addr_with_none_subaddr.replace(
        "qqqqqqqqqqqqq", "qqqqqqqqqqq")
    with pytest.raises(ValueError):
        identifier.decode_account(short_encoded_address, hrp)

    # fail to decode larger payload
    large_encoded_address = enocded_addr_with_none_subaddr.replace(
        "qqqqqqqqqqqqq", "qqqqqqqqqqqqqq")
    with pytest.raises(ValueError):
        identifier.decode_account(large_encoded_address, hrp)

    # fail to decode invalid separator
    invalid_separator_encoded_address = enocded_addr_with_none_subaddr.replace(
        "1p7", "0p7")
    with pytest.raises(ValueError):
        identifier.decode_account(invalid_separator_encoded_address, hrp)

    # fail to decode invalid character
    invalid_char_encoded_address = enocded_addr_with_none_subaddr.replace(
        "1p7", "1pb")
    with pytest.raises(ValueError):
        identifier.decode_account(invalid_char_encoded_address, hrp)
def test_new_payment_request_and_object(factory):
    sender = LocalAccount.generate()
    receiver = LocalAccount.generate()
    payment = factory.new_payment_object(sender, receiver)
    request = offchain.new_payment_request(payment)
    reference_id = payment.reference_id

    assert reference_id
    assert_cid(request.cid)
    assert uuid.UUID(reference_id)
    assert "-" in reference_id

    payment = offchain.from_dict(request.command, offchain.PaymentCommandObject).payment
    address, subaddress = identifier.decode_account(payment.sender.address, identifier.TDM)
    assert subaddress is not None
    assert address == sender.account_address
    address, subaddress = identifier.decode_account(payment.receiver.address, identifier.TDM)
    assert subaddress is not None
    assert address == receiver.account_address

    expected = f"""{{
  "cid": "{request.cid}",
  "command_type": "PaymentCommand",
  "command": {{
    "_ObjectType": "PaymentCommand",
    "payment": {{
      "reference_id": "{reference_id}",
      "sender": {{
        "address": "{payment.sender.address}",
        "status": {{
          "status": "needs_kyc_data"
        }},
        "kyc_data": {{
          "type": "individual",
          "payload_version": 1,
          "given_name": "Jack",
          "surname": "G",
          "address": {{
            "city": "San Francisco"
          }}
        }}
      }},
      "receiver": {{
        "address": "{payment.receiver.address}",
        "status": {{
          "status": "none"
        }}
      }},
      "action": {{
        "amount": 1000000000000,
        "currency": "XUS",
        "action": "charge",
        "timestamp": {payment.action.timestamp}
      }}
    }}
  }},
  "_ObjectType": "CommandRequestObject"
}}"""
    assert json.loads(offchain.to_json(request)) == json.loads(expected)
    assert request == offchain.from_json(expected, offchain.CommandRequestObject)
    assert request == offchain.from_json(expected)
def test_my_subaddress(factory):
    cmd = factory.new_sender_payment_command()
    _, subaddress = identifier.decode_account(cmd.payment.sender.address,
                                              factory.hrp())
    assert cmd.my_subaddress(factory.hrp()) == subaddress
async def test_generate_account_identifier(target_client: RestClient,
                                           hrp: str) -> None:
    account = await target_client.create_account()
    account_identifier = await account.generate_account_identifier()
    account_address, _ = identifier.decode_account(account_identifier, hrp)
    assert account_address