Skip to content
This repository has been archived by the owner on Dec 12, 2024. It is now read-only.

Latest commit

 

History

History
602 lines (430 loc) · 30.8 KB

README.md

File metadata and controls

602 lines (430 loc) · 30.8 KB

Decentralized Agnostic Paytag (DAP)

* Title: Decentralized Agnostic Paytags
* Created: 2024-04-12
* Status: Draft
* Contributors: Moe Jangda [twitter: @moegrammer] (Author)
                Andrew Parker []
                Chris Myers []
                Frank Hinek []
                Jeremy Mawson []

Table of Contents

Problem Statement

Countless applications exist worldwide (e.g. CashApp, YellowPay, Bitso, Coinbase, Chipper Cash, Paypal, Venmo etc.) that allow individuals to send and receive money using a variety of payment methods and financial instruments. These apps often provide a P2P function that allows individuals using the same app to send and receive money to/from one another using memorable handles, paytags or aliases. These paytags however are only useful within the context of the app they are created in and cannot be used to send and receive money to/from individuals using other apps or services. Sending the same currency accross different apps or payment networks is a notoriously cumbersome and error-prone process that often involves copy / pasting long, confusing, and nuanced mumbo jumbo. Further, depending on the type of currency being used, the process can be even more complex. For example:

  • sending BTC on Lightning is different than sending BTC on mainnet
  • sending USDC on Ethereum has different details and nuances than sending USDC on Stellar.
  • sending fiat money to a mobile money wallet is different than sending it to a bank account.

What this leaves us with are many large payment networks that all exist on their own islands. Life is great on the island but getting money onto and off the island is a "yeet and hope for the best" kind of situation. Further, indviduals are left to navigate and understand the nuances of whatever currency they're hoping to use. This is a problem because it makes it difficult for individuals to use the currency of their choice in the way that they want to. Moreover, an inevitable lengthy conversation occurs between two people prior to sending any money, in order to figure out what the hell one another even has or can use to send and receive money. As a result, individuals end up with 18 wallets and a tense negotiation about which one to use everytime they need to send or receive.

Objective

Important

Alice should be able to send money to Bob using a memorable handle (aka DAP) regardless of what app or currency is being used. A DAP should be simple yet distinct from other widely recognized handles: @handle/domain e.g. @moegrammer/didpay.me.

The conversation between Alice and Bob should be as simple as:

Alice: Yo Bobby boy! Thanks for the coffee. What's your DAP so i can pay you back?
Bob: Anytime. DAP me up at @waterboy/cash.app

Alice should then be able to pop open whatever app she uses, type in bob's DAP, and send him the money. Bob should then be able to receive the money in whatever app he uses. Hypothetical examples of this are:

  • CashApp -> YellowPay (or vice versa)
  • Chipper Cash -> Bitso (or vice versa)
  • Coinbase -> CashApp (or vice versa)
  • CashApp -> Self Custodial Wallet (or vice versa)
  • Paper Wallet -> Paper Wallet

More concretely, The objective of this specification is to provide a standardized means to:

  • express a money address
  • associate any number of money addresses to a resolvable identifier (DID)
  • register the identifier with a handle at any/many DAP registries (e.g. registering did:dht:3456787654323rfvcxaw34tf with CashApp under the handle moegrammer results in @moegrammer/cash.app)
  • resolve a DAP to a DID

Requirements

  • Any currency should be expressable as a money address
  • Any individual or institution MUST be able to run their own DAP Registry
  • An individual MUST have the ability to custody and control their own resolvable identifier

TL;DR How it Works

Note

Link to diagram available here

DAP

A Decentralized Agnostic Paytag (DAP) is a human-friendly email-like handle that can be shared between individuals for the purposes of sending and recieving money. More specifically, a DAP can be resolved to retrieve information about what currencies and payment networks an individual can receive money on.

A DAP resolves to a Decentralized Identifier (DID) by the registry associated to the domain portion of the DAP. The resulting DID is then resolved to retrieve all of the active money addresses where the individual is able to receive payment.

Important

DAPs have nothing to do with the actual mechanics of sending and receiving money. They are simply a means to easily retrieve information about how to send and receive money to/from an individual.

A DAP is composed of a local handle and a domain and is structured as follows:

@local-handle/domain

Note

We've chosen a format that is notably distinct from email addresses in order to prevent confusion between the two particularly within contexts where email addresses are already in use.

Local Handles

A handle is unique to the domain that it is registered at. The handle itself is NOT globally unique. The entire DAP itself however, is globally unique. As such, restrictions on the format are left to the domain that the DAP is registered at. If no pre-existing format is defined, the following is recommended:

  • UTF-8 Encoding: All characters should be UTF-8 encoded to support internationalization.
  • Character Exclusions: The local handle must not include unicode control characters or punctuation characters.
  • Length Restrictions: The local handle must be between 3 to 30 characters in length.

Note

providing the flexibility for domains to define their own formats was decided upon in order to support pre-existing systems that already have their own formats.

Domain

The domain portion of a DAP is used to identify the registry at which the DAP was registered. The domain is used to resolve the DAP to a DID.

Resolution

  1. split the DAP into two parts, localHandle and domain using the last / as the delimiter. The @ is not considered to be part of the local handle.
  2. Construct a did:web DID using the domain as the method-specific identifier.
  3. Resolve the resulting DID to retrieve the DID Document.
  4. Find the service of type DAPRegistry in the DID Document.
  5. Use the serviceEndpoint of the resulting service to construct the following URL: <serviceEndpoint>/daps/<local-handle>.
  6. Make a GET request to the constructed URL.
  7. The response will contain the DID associated with the DAP.
  8. Resolve the DID to retrieve the DID Document.
  9. Find all of the MoneyAddress services in the DID Document.

Example

For the DAP @moegrammer/cash.app:

  1. Split into local handle moegrammer and domain cash.app.
  2. Construct a did:web DID: did:web:cash.app.
  3. Resolve did:web:cash.app to get the DID Document.
  4. Find the DAPRegistry service in the DID Document.
  5. Use the service endpoint to construct the URL: <serviceEndpoint>/daps/moegrammer.
  6. Make a GET request to this URL.
  7. The response contains the DID associated with moegrammer/cash.app.
  8. Resolve this DID to get the DID Document.
  9. Find all MoneyAddress services in the DID Document.

Money Address

A money address is a Uniform Resource Name (URN) that represents a means through which an individual can be payed a specific currency. The URN is structured as follows:

urn:<currency_code>:<curr_specific_part>

Examples

USDC on Ethereum

urn:usdc:eth:0x1234567890abcdef1234567890abcdef12345678

BTC On-chain Address

urn:btc:addr:1LMcKyPmwebfygoeZP8E9jAMS2BcgH3Yip

BTC Lightning Offer (BOLT 12)

urn:btc:lno:lno1zrxq8pjw7qjlm68mtp7e3yvxee4y5xrgjhhyf2fxhlphpckrvevh50u0qfq6umvtqucas8054gtms4kwlx204hjh3dlfjv4qusv4u6ft35896qsrjr26m4aqma27vsvg3z5rcqfpxezttla82k4ydrca02e0huc7qkhsqvu7x90y89m423eaph3j5jcksgqmv5894cmxcxrqpryxspx7fhme3l7nf0zlsh56ec3dc3qgng8wf58zngzrqd8f09zvjx7srcafrhhd55wkx3p04zwf29yee46afgyp8mxvedscqqqsxurgnea7wdc0a6dcktn0wswfdc

BTC Lightning Address

urn:btc:lnaddr:[email protected]

BTC LNURL

urn:btc:lnurl:https://someurl.com

BTC Silent Payment Address (BIP-0352)

urn:btc:spaddr:sp1qqweplq6ylpfrzuq6hfznzmv28djsraupudz0s0dclyt8erh70pgwxqkz2ydatksrdzf770umsntsmcjp4kcz7jqu03jeszh0gdmpjzmrf5u4zh0c

BTC DNS (BIP-0353)

urn:btc:dns:[email protected]

KES Mobile Money

urn:kes:momo:mpesa:254712345678

Currency Specific Parts

Important

from a standards perspective, a Currency Specific Part is an arbitrary string. There are no constraints or limitations. However, in order for Money Addresses to be of any practical use, conventions will need to be established for each currency. A separate registry will be maintained for currency specific parts as these conventions emerge.

This specification proposes Currency Specific Parts for USDC and BTC.

USDC

Network specific Address
Format

urn:usdc:<network>:<address>

Examples
URN CSP Description
urn:usdc:eth:0x1234567890abcdef1234567890abcdef12345678 eth:0x1234567890abcdef1234567890abcdef12345678 USDC address on Ethereum
urn:usdc:xlm:0xff35866aCb80ce4b169d1460cd48108955c1c445 xlm:0xff35866aCb80ce4b169d1460cd48108955c1c445 USDC address on Stellar

BTC

On-chain Address
Format

urn:btc:addr:<address>

Lightning Offer (BOLT 12)

urn:btc:lno:<offer>

Lightning Address
Format

urn:btc:lnaddr:<lightning-address>

LNURL
Format

urn:btc:lnurl:<url>

Silent Payment Address
Format

urn:btc:spaddr:<address>

DNS (BIP-353)
Format

urn:btc:dns:<internet-identifier>

DID Resource

A Money address is associated to a DID by representing it as a Service entry on a DID Document.

Note

Services are used in DID documents to express ways of communicating with the respected DID subject. A service can be any type of service the DID subject wants to advertise, including decentralized identity management services for further discovery, authentication, authorization, or interaction.

Money addresses are represented as a service of type MoneyAddress in the DID Document. The service is structured as follows:

field value description
type MoneyAddress MUST always be MoneyAddress
id []string an abitrary string that can be used to uniquely identify the money address
serviceEndpoint []string 1 or more payment address URNs

Note

Any number of money addresses can be associated with a DID. They can be represented as individual service entries in the DID Document.

Examples

Example Service Entry

{
  "type": "MoneyAddress",
  "id": "#some-id",
  "serviceEndpoint": ["urn:<currency_code>:<curr_specific_part>"]
}

Example DID Document

Note

Other fields in the DID Document have been omitted for brevity

{
  "@context": "https://www.w3.org/ns/did/v1",
  "id": "did:dht:123456789abcdefghi",
  "service": [
    {
      "type": "MoneyAddress",
      "id": "#some-id",
      "serviceEndpoint": ["urn:usdc:eth:0x1234567890abcdef1234567890abcdef12345678"]
    }
  ]
}

DAP Registry

A DAP Registry is a service that is responsible for maintaining a mapping of local handles to DIDs registered at the domain hosting the registry. The service provides two primary operations:

  1. Associate a local handle with a DID (a.k.a DAP Registration)
  2. Resolve a DAP to a DID.

A DAP Registry can be hosted by any individual or institution that controls a domain. In order to provide a DAP Registry a domain MUST:

  • host a resolvable DID Document per the did:web specification.
    • e.g. did:web:cash.app resolves to https://cash.app/.well-known/did.json
  • advertise the DAP Registry as a service in the domain's DID Document e.g.
{
  "id": "did:web:cash.app",
  "service": [
    {
      "type": "DAPRegistry",
      "serviceEndpoint": ["https://dap.cash.app"],
      "id": "#some-id"
    }
  ]
}

Note

the value of serviceEndpoint is the base URL of the domain's DAP Registry HTTP API and can be any URL that the domain owner chooses.

Registry HTTP API

All DAP Registries MUST provide an HTTP API that adheres to the following specifications:

CORS Policy

The DAP Registry MUST have a CORS policy that allows requests from any origin. This is to ensure that a registry can be accessed by any app or service that wishes to resolve a DAP or facilitate registration (if the registry allows it).

Responses

All responses from the DAP Registry MUST be a JSON Object that contains the following entries:

Field Data Type Required Description
data any N set to whatever an endpoint is supposed to return on success
error Error N set if an error occurs

Important

data and error are mutually exclusive. Only one of them should be present in a response.

Error

An Error object is a JSON object that contains the following entries:

Field Data Type Required Description
message string Y human readable message that describes the error

The following headers MUST be included in every response:

header value
Content-Type application/json

DAP Resolution

Warning

TODO: Fill out

Request

Method: GET


URL: <serviceEndpoint>/daps/<local-handle>


Response

Field Data Type Required Description
did string Y The DID associated to the DAP provided in the request
proof string N Signed payload sent when the DID was registered
Errors
Not Found

Status Code: 404

DAP Registration

The DAP Registration endpoint is used to register a local handle with a DID at the domain hosting the registry.

Important

This endpoint is NOT required to be implemented by all DAP Registries. It is up to the domain owner to decide if they want to allow individuals to register their own DIDs with their handles.

You can determine whether a registry permits registration by checking the metadata endpoint.

Request

Method: POST


URL: <serviceEndpoint>/daps


Body

The body of the request is a JSON object that contains the following properties:

Field Data Type Required Description
id string Y typeid with prefix reg
domain string Y The domain that the handle is being registered at
handle string Y desired handle
did string Y DID being registered
signature string Y Compact JWS with detached content. See here for more details

Response

Status: 202: Accepted

Warning

TODO: Fill out

Response should include a signature over the registration request digest. The signature MUST be computed using a private key associated to the registry's DID.

TODO: Include 303: See Other with a Location header that contains a URL for further authentication. This is to allow for scenarios where the registry is a pre-existing app that provides handles to individuals via their sign up process and wants to allow individuals to associate their own DIDs with their handles.

Signature

The Registration Request signature is a detached content compact JWS as defined in RFC-7515 (Detached content defined in appendix F).

Note

Why detatched JWS? Detached signatures are employed to facilitate scenarios where the payload (i.e., the data being signed) is already available or transmitted separately. By using a detached signature, the original payload remains unaltered and can be transmitted or stored in its native format without being embedded in the signature itself. This approach avoides redundancy but also allows recipients to independently verify the integrity and authenticity of the payload using the detached signature and the signer's public key.

The signature is computed using a private key whose public key is present as a verification method with an assertion method verification relationship when resolving the DID being registered.

Perform the following steps to compute the signature:

  1. Construct the JWS Header as defined here in RFC7515. The header MUST include the following properties:
  2. Compute the registration request's digest and use it as the JWS Payload
  3. Compute the JWS as defined here in RFC7515
  4. detach the payload as defined here in RFC7515
  5. set the value of the signature property to the resulting compact detached JWS

Digest

A digest is a representation of data in a condensed form. When used in cryptographic contexts, this condensed form provides a way to verify the integrity and authenticity of data without having to compare the data in its entirety.

perform the following steps to compute the registration request's digest:

  1. Initialize payload to be a json object that contains all of the registration request's properties except for the signature property
  2. JSON serialize payload using the JSON Canonicalization Scheme (JCS) as defined in RFC-8785
  3. compute the sha256 hash of the serialized payload
  4. base64url encode the hash without padding as defined in RFC-7515

Note

Why JSON Canonicalization Scheme (JCS)?

  • Consistency: JSON does not guarantee property order. Two semantically identical JSON objects can have their properties serialized in different orders. This can lead to two different serialized strings for the same data. When performing cryptographic operations, such as creating a digital signature or a hash, even the slightest difference in input data results in a drastically different output. Therefore, a consistent, canonical form of the data is essential.

  • Interoperability: Different implementations and libraries serialize JSON differently. JCS ensures that different systems and libraries produce the same serialized output for the same input, thus facilitating interoperability.

  • Avoidance of Data Ambiguity: JSON allows for multiple valid representations of the same data (e.g., the use of whitespace, number representations). This can cause ambiguity in interpreting or processing such data, especially in cryptographic contexts where precision is paramount. JCS defines a single, unambiguous representation for any given JSON document.

  • Security: In the world of security, particularly with cryptographic signatures, the principle of "what you see is what you sign" is critical. If there's any ambiguity in the serialized data form, it can lead to potential vulnerabilities or issues. By ensuring a canonical format, JCS mitigates potential attack vectors related to JSON's flexibility.

  • Compatibility with Existing JSON Parsers: JCS is designed to work seamlessly with existing JSON parsers. This makes it relatively straightforward to integrate into systems already using JSON without requiring significant changes to the existing infrastructure.

Note

Why SHA256?

  • Widely Recognized and Adopted: SHA256, which is part of the SHA-2 (Secure Hash Algorithm 2) family, is widely recognized and adopted in various cryptographic applications and protocols. SHA256 is standardized by the National Institute of Standards and Technology (NIST) in the U.S. Being a standard means it has undergone extensive review and evaluation by experts in the field.
  • Security: As of today, SHA256 has no known vulnerability to collision attacks, preimage attacks, or second preimage attacks.
    • A collision attack is when two different inputs produce the same hash.
    • A preimage attack is when, given a hash, an attacker finds an input that hashes to it.
    • A second preimage attack is when, given an input and its hash, an attacker finds a different input that produces the same hash.
  • Output Size: SHA256 provides a fixed hash output of 256 bits (32 bytes). This size strikes a balance between efficiency and security

Note

Why Base64url?

When sending a SHA-256 hash (or any binary data) over the wire, it's common to use an encoding that translates the binary data into a set of characters that can be safely transmitted over systems that might not handle raw binary well.

Base64url-encoded data is safe for transmission over most protocols and systems since it only uses printable ASCII characters. It is widely supported across several programming languages.

A raw SHA256 hash is 32 bytes. When base64 encoded it becomes a 44 byte string

Metadata

The metadata endpoint is used to retrieve information about the DAP Registry (e.g. what operations are supported)

Request

Method: GET


URL: <serviceEndpoint>/metadata


Response

The response is a JSON object that contains the following properties:

Field Data Type Required Description
registration RegistrationMetadata Y
RegistrationMetadata

The RegistrationMetadata object contains the following properties:

Field Data Type Required Description
enabled boolean Y Indicates whether registration is enabled
supportedDidMethods []string N Supported DID Methods

Modes of Operation

Bring Your Own DID

DAP Registries are designed to allow individuals to associate their own DIDs (a.k.a DIDs they control) with a handle at a registry. Details on how registration works can be found here. It is entirely up to the registry operator to decide whether they want to enable this feature. Registries that intend to support self-custodial use cases must enable registration.

Registry-provided DIDs

Pre-existing apps that already provide individuals with usernames, handles, or paytags can provision a DAP for each pre-existing handle by making it resolvable as a did:web (e.g. did:web:<domain>:<handle>). Resolving this DID returns a DID Document that contains all of the money addresses provided by the app for the individual. This allows the app to provide individuals with a DAP that can be used to send and receive money to/from the app.

Example

CashApp is a pre-existing app that provides individuals with CashTags (e.g. $moegrammer) and decides to adopt DAPs by standing up a DAP Registry.

Per the DAP Registry section of this specification, CashApp creates a did:web (specifically did:web:cash.app) by hosting a DID Document at https://cash.app/.well-known/did.json and advertises their Registry as a service in the DID Document.

CashApp makes each CashTag resolvable as a did:web (e.g. did:web:cash.app:moegrammer) per the did:web specification for resolution by responding to requests to https://cash.app/moegrammer/did.json with a DID Document specific to the CashTag. The resulting DID Document contains two money address service entries: one that includes a BTC address, and another that includes an LNURL.

As a result, every CashTag can now be used as a DAP. @moegrammer/cash.app is resolved using CashApp's DAP Registry to did:web:cash.app:moegrammer which in turn resolves to the DID Document that contains the aforementioned money addresses.

Any app that supports sending money to a DAP can now send BTC via L1 or Lightning directly to any CashTag without the sender having to ask the recipient for a lightning invoice or what their BTC address is. The only information the sender needs is the recipient's DAP (e.g. [email protected]). The sender's app will take care of the rest. As is the case with receiving BTC on CashApp today, the recipient will receive a push notification when their BTC arrives.

Note

This is not too far fetched as CashApp already provides unique resolvable URLs per CashTag e.g. https://cash.app/$moegrammer that can be used to retrieve a CashApp specific QR code.

Lasty, CashApp can allow individuals to BYODID (Bring Your Own DID) by enabling Registration at their DAP Registry. This would allow individuals to associate a DID they control with their CashTag.

Wombo Combo

A DAP Registry can support both modes of operation. This allows individuals to associate their own DIDs with a handle at the registry while also allowing the registry to provide DIDs for individuals that do not have one.

Privacy Considerations

Warning

TODO: Fill out

FAQ

How is this different from UMA (Universal Money Address)?

Warning

TODO: Fill out

Where did the name DAP come from?

Warning

TODO: Fill out

What makes this solution decentralized?

Warning

TODO: Fill out