From 915859213c6141bf5cf9923c07b1749ffa629018 Mon Sep 17 00:00:00 2001 From: SunMi Lee Date: Fri, 23 Apr 2021 00:25:42 -0700 Subject: [PATCH 1/3] Add reference ID exchange object types --- src/diem/offchain/types/command_types.py | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/diem/offchain/types/command_types.py b/src/diem/offchain/types/command_types.py index d61c0885..6f6360ac 100644 --- a/src/diem/offchain/types/command_types.py +++ b/src/diem/offchain/types/command_types.py @@ -79,6 +79,12 @@ class ErrorCode: # Field payment.action.currency value is a valid Diem currency code, but it is not supported / acceptable by the receiver VASP. unsupported_currency = "unsupported_currency" + # Duplicate Reference ID was rejected by the receiving end + duplicate_reference_id = "duplicate_reference_id" + + # Receiving end could not find the user with the given user_identifier + invalid_receiver = "invalid_receiver" + class OffChainErrorType: """command_error occurs in response to a Command failing to be applied - @@ -90,6 +96,12 @@ class OffChainErrorType: protocol_error = "protocol_error" +class OffChainCommandResponseResultType: + """ Type of result in a CommandResponseObject""" + + ReferenceIDCommandResponse = "ReferenceIDCommandResponse" + + @dataclass(frozen=True) class FundPullPreApprovalCommandObject: _ObjectType: str = datafield(metadata={"valid-values": [CommandType.FundPullPreApprovalCommand]}) @@ -124,6 +136,19 @@ class OffChainErrorObject: message: typing.Optional[str] = datafield(default=None) +@dataclass(frozen=True) +class OffChainResultObject: + _ObjectType: str = datafield( + metadata={ + "valid-values": [ + OffChainCommandResponseResultType.ReferenceIDCommandResponse, + ] + } + ) + # ReferenceIDCommandResponse: Receiver's onchain account identifier + receiver_address: typing.Optional[str] = datafield(default=None) + + @dataclass(frozen=True) class CommandResponseObject: # Either success or failure. @@ -134,3 +159,5 @@ class CommandResponseObject: error: typing.Optional[OffChainErrorObject] = datafield(default=None) # The Command identifier to which this is a response. cid: typing.Optional[str] = datafield(default=None) + # An result JSON object that may be defined when status == "success" + result: typing.Optional[OffChainResultObject] = datafield(default=None) From e042bea789df49844c81e64bc800bb92958d6dc1 Mon Sep 17 00:00:00 2001 From: SunMi Lee Date: Fri, 23 Apr 2021 01:04:14 -0700 Subject: [PATCH 2/3] Add reference ID command class --- src/diem/offchain/reference_id_command.py | 59 +++++++++++++++++++++++ src/diem/offchain/types/command_types.py | 20 ++++++-- 2 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 src/diem/offchain/reference_id_command.py diff --git a/src/diem/offchain/reference_id_command.py b/src/diem/offchain/reference_id_command.py new file mode 100644 index 00000000..17794336 --- /dev/null +++ b/src/diem/offchain/reference_id_command.py @@ -0,0 +1,59 @@ +# Copyright (c) The Diem Core Contributors +# SPDX-License-Identifier: Apache-2.0 + +"""This module defines `ReferenceIDCommand` class provides utils for processing `ReferenceIDCommand` properly.""" + +import typing, dataclasses, uuid +from .types import ( + ReferenceIDCommandObject, +) + + +@dataclasses.dataclass(frozen=True) +class ReferenceIDCommand: + """Wrapper object of `ReferenceIDCommand` with request information + + Defined in DIP-10: https://github.com/diem/dip/blob/main/dips/dip-10.md + """ + + reference_id_command_object: ReferenceIDCommandObject + cid: str = dataclasses.field(default_factory=lambda: str(uuid.uuid4())) + + @staticmethod + def init( + sender: str, + sender_address: str, + receiver: str, + reference_id: str, + ) -> "ReferenceIDCommand": + """init functon initializes a new `ReferenceIDCommand` for starting the DiemID to address resolution""" + + return ReferenceIDCommand( + reference_id_command_object=ReferenceIDCommandObject( + sender=sender, sender_address=sender_address, receiver=receiver, reference_id=reference_id + ) + ) + + def id(self) -> str: + """returns `cid` from the request object""" + return self.cid + + def reference_id(self) -> str: + """returns `reference_id` of `ReferenceIDCommand`""" + + return self.reference_id_command_object.reference_id + + def sender(self) -> str: + """returns sender pay address""" + + return self.reference_id_command_object.sender + + def receiver(self) -> str: + """returns receiver pay address""" + + return self.reference_id_command_object.receiver + + def sender_address(self) -> str: + """returns sender address""" + + return self.reference_id_command_object.sender_address diff --git a/src/diem/offchain/types/command_types.py b/src/diem/offchain/types/command_types.py index 6f6360ac..d45e0551 100644 --- a/src/diem/offchain/types/command_types.py +++ b/src/diem/offchain/types/command_types.py @@ -15,6 +15,7 @@ class CommandType: PaymentCommand = "PaymentCommand" FundPullPreApprovalCommand = "FundPullPreApprovalCommand" PingCommand = "PingCommand" + ReferenceIDCommand = "ReferenceIDCommand" class CommandResponseStatus: @@ -137,7 +138,7 @@ class OffChainErrorObject: @dataclass(frozen=True) -class OffChainResultObject: +class ReferenceIDCommandResultObject: _ObjectType: str = datafield( metadata={ "valid-values": [ @@ -146,7 +147,7 @@ class OffChainResultObject: } ) # ReferenceIDCommandResponse: Receiver's onchain account identifier - receiver_address: typing.Optional[str] = datafield(default=None) + receiver_address: str @dataclass(frozen=True) @@ -160,4 +161,17 @@ class CommandResponseObject: # The Command identifier to which this is a response. cid: typing.Optional[str] = datafield(default=None) # An result JSON object that may be defined when status == "success" - result: typing.Optional[OffChainResultObject] = datafield(default=None) + result: typing.Optional[dict] = datafield(default=None) # pyre-ignore + + +@dataclass(frozen=True) +class ReferenceIDCommandObject: + _ObjectType: str = datafield(metadata={"valid-values": [CommandType.ReferenceIDCommand]}) + # Sender's full DiemID + sender: str + # Sender's onchain account identifier with subaddress set to `None` or the zero subaddress + sender_address: str + # Receiver's full DiemID + receiver: str + # Reference ID of this transaction + reference_id: str From ca7c0339a7b1f5045c0ccb7f6826f8945a00667f Mon Sep 17 00:00:00 2001 From: SunMi Lee Date: Tue, 27 Apr 2021 00:14:45 -0700 Subject: [PATCH 3/3] Add test --- src/diem/offchain/__init__.py | 2 ++ src/diem/offchain/reference_id_command.py | 2 +- src/diem/offchain/types/__init__.py | 2 ++ src/diem/offchain/types/command_types.py | 13 +++++++----- tests/test_offchain_types.py | 25 +++++++++++++++++++++++ 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/diem/offchain/__init__.py b/src/diem/offchain/__init__.py index 58c1ec7c..72e9a99c 100644 --- a/src/diem/offchain/__init__.py +++ b/src/diem/offchain/__init__.py @@ -20,6 +20,8 @@ PaymentObject, PaymentActorObject, PaymentActionObject, + ReferenceIDCommandResultObject, + ReferenceIDCommandObject, Status, StatusObject, KycDataObjectType, diff --git a/src/diem/offchain/reference_id_command.py b/src/diem/offchain/reference_id_command.py index 17794336..7195a287 100644 --- a/src/diem/offchain/reference_id_command.py +++ b/src/diem/offchain/reference_id_command.py @@ -3,7 +3,7 @@ """This module defines `ReferenceIDCommand` class provides utils for processing `ReferenceIDCommand` properly.""" -import typing, dataclasses, uuid +import dataclasses, uuid from .types import ( ReferenceIDCommandObject, ) diff --git a/src/diem/offchain/types/__init__.py b/src/diem/offchain/types/__init__.py index 96a8f3e6..80944092 100644 --- a/src/diem/offchain/types/__init__.py +++ b/src/diem/offchain/types/__init__.py @@ -14,6 +14,8 @@ OffChainErrorObject, CommandRequestObject, CommandResponseStatus, + ReferenceIDCommandObject, + ReferenceIDCommandResultObject, FundPullPreApprovalCommandObject, UUID_REGEX, ) diff --git a/src/diem/offchain/types/command_types.py b/src/diem/offchain/types/command_types.py index d45e0551..38442301 100644 --- a/src/diem/offchain/types/command_types.py +++ b/src/diem/offchain/types/command_types.py @@ -139,15 +139,16 @@ class OffChainErrorObject: @dataclass(frozen=True) class ReferenceIDCommandResultObject: + # ReferenceIDCommandResponse: Receiver's onchain account identifier + receiver_address: str _ObjectType: str = datafield( + default=OffChainCommandResponseResultType.ReferenceIDCommandResponse, metadata={ "valid-values": [ OffChainCommandResponseResultType.ReferenceIDCommandResponse, ] - } + }, ) - # ReferenceIDCommandResponse: Receiver's onchain account identifier - receiver_address: str @dataclass(frozen=True) @@ -161,12 +162,11 @@ class CommandResponseObject: # The Command identifier to which this is a response. cid: typing.Optional[str] = datafield(default=None) # An result JSON object that may be defined when status == "success" - result: typing.Optional[dict] = datafield(default=None) # pyre-ignore + result: typing.Optional[dict] = datafield(default=None) # pyre-ignore @dataclass(frozen=True) class ReferenceIDCommandObject: - _ObjectType: str = datafield(metadata={"valid-values": [CommandType.ReferenceIDCommand]}) # Sender's full DiemID sender: str # Sender's onchain account identifier with subaddress set to `None` or the zero subaddress @@ -175,3 +175,6 @@ class ReferenceIDCommandObject: receiver: str # Reference ID of this transaction reference_id: str + _ObjectType: str = datafield( + default=CommandType.ReferenceIDCommand, metadata={"valid-values": [CommandType.ReferenceIDCommand]} + ) diff --git a/tests/test_offchain_types.py b/tests/test_offchain_types.py index 354cdfb2..4f79aab6 100644 --- a/tests/test_offchain_types.py +++ b/tests/test_offchain_types.py @@ -421,6 +421,31 @@ def test_missing_required_field(): ) +def test_reference_id_command_result_object(): + # Test can encode and decode correct response format + ref_id_command_result = offchain.ReferenceIDCommandResultObject( + receiver_address="dm1p7ujcndcl7nudzwt8fglhx6wxnvqqqqqqqqqqqqelu3xv", + ) + response = offchain.CommandResponseObject( + status=offchain.CommandResponseStatus.success, + cid="3185027f-0574-6f55-2668-3a38fdb5de98", + result=offchain.to_dict(ref_id_command_result), + ) + assert offchain.from_json(offchain.to_json(response), offchain.CommandResponseObject) == response + assert offchain.from_json(offchain.to_json(response)) == response + assert json.loads(offchain.to_json(response)) == json.loads( + """{ + "status": "success", + "_ObjectType": "CommandResponseObject", + "cid": "3185027f-0574-6f55-2668-3a38fdb5de98", + "result": { + "_ObjectType": "ReferenceIDCommandResponse", + "receiver_address": "dm1p7ujcndcl7nudzwt8fglhx6wxnvqqqqqqqqqqqqelu3xv" + } + }""" + ) + + def assert_cid(cid: str): assert isinstance(cid, str) assert uuid.UUID(cid)