Skip to content

Latest commit

 

History

History
 
 

XLS-5

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
  XLS-5 - XRP Ledger Proposed Standard #5

  Title:        Tagged Addresses
  Author:       Nikolaos D. Bougalis 
  Affiliation:  Ripple
  Created:      2019-05-15

  Original: https://github.com/xrp-community/standards-drafts/issues/6

Abstract

Destination tags provide a way for exchanges, payment processors, corporates or entities which accept incoming payments, escrows, checks and similar transcations to use a single receiving wallet while being able to disambiguate incoming transactions by instructing the senders to include a destination tag.

This draft introduces the concept of a tagged address: an address that contains both the target wallet as well as a destination tag as a single unit.

Motivation

Although flexible, destination tags suffer from several drawbacks.

Communicating a destination tag to users can be a problem for a number of reasons:

  • In the absence of a standard format to represent an (address, tag) pair, different users of destination tags communicate the information in different ways.
  • Users needs to enter two items, usually in two distinct input fields which may be inconsistently or confusingly named (e.g. one implementation refers to the destination tag as a "PIN").
  • Programmers need to decide on how, when or even if the "Destination Tag" field must be surfaced, complicating panel layouts and increasing the potential for user confusion.
  • The tag is an opaque identifier, generated by and meaningful only to the entity that will be receiving funds. Tags do not natively include any kind of checksum or error checking to prevent against common issues such as a user accidentially omitting or transposing digits or otherwiseincorrectly enter a tag.

This proposal seeks to address this problem by defining a standard format to represent an (address, tag) which:

  • Is expressed as a single string that shares all the desirable properties of existing addresses, including the ability to be selected by double-clicking on them.
  • Eliminates the need for a separate "Destination Tag" field by allowing a single format to represent addresses both with and without tags.
  • Includes a built-in checksum as a form of error-checking to reduce the probability that a typographical error will generate a correct address.

Limitations

We are not looking to change the on-ledger format; that is, the new style addresses can't be used for fields where an AccountID is expected in the binary format. Instead, the packed address will be detected and decoded at higher levels (for example, by the client sofware, ripple-lib or the RPC and WebSocket APIs in rippled), verified and then split into distinct fields (e.g. sfDestination and sfDestinationTag) as appropriate, to assemble the underlying transaction.

Philosophy

“The nature of Bitcoin is such that once version 0.1 was released, the core design was set in stone for the rest of its lifetime.”

— Satoshi Nakamoto

While we can propose a standard, we need to contend with the fact that, by design, the basics of the protocol (and that includes account addresses) were fixed the moment the ledger was instantiated. While not completely inflexible, account addresses are deeply embedded into the protocol and even if they aren't part of the "core design" changing them would involve a huge amount of pain.

Not all is lost, however and there are things we can do. In this case, we can define a new address style which incorporates the destination tag and software can be understand such packed addresses, improving the UX for users, while intelligently unpacking such addresses into their constituent parts for the underlying system.

Options

Although we have options in developing a new format, including what encoding to use, ideally the resulting addresses will be similar to existing addresses to reduce the likelihood of user confusion as much as possible and, ideally, not requiring developers to implement a new codec.

Given this constrain, we need to use the Base58Check encoding, leaving us with two options:

  1. A "tightly packed" format, where the address and tag are encoded using Base58Check as a single unit.
  2. A "loosely packed" format, where the address is encoded using Base58Check and the destination tag is encoded separately and appended to the address.

The advantage of the "loose" format is that a tagged address will precisely match the classic address, up to the tag. The "tight" format results in an address that shares no common prefix with the classic address, except, perhaps, by chance.

The disadvantage of the "loose" format is that a tagged address is more complex to detect, encode and decode.

On balance, we feel that although the "loose" could allow for classic and tagged addresses to co-exist, we believe that the "tight" format is a better choice overall.

Status

This is only a proposal. It is my hope that it will generate discussion between developers, community members and other interested parties, and that we will reach consensus on the way forward.

Comments, criticisms, suggestions and improvements are welcome!

Proposal

Addresses and Destination Tags

For a better introduction to addresses and tags, it may help to reference https://developers.ripple.com/accounts.html.

Existing Format

Currently, an address is generated by base58-encoding (with a checksum) a 21 byte buffer:

[← 1 byte prefix →|← 160 bits of account ID →]

The chosen byte prefix is TokenType::AccountID (value 0)

Proposed format

In the proposed format, the address and destination tag are combined into a single 30 byte buffer which is then encoded using the familiar Base58Check algorithm as a single unit, using a new two-byte prefix:

[← 2 byte prefix →|← 160 bits of account ID →|← 8 bits of flags →|← 64 bits of tag →]

The tag shall always be encoded as a little endian two's complement 64 bit integer.

Prefix

The standard proposes using different prefixes, making addresses encoded for use on the mainnet have a different initial character than addresses encoded for use on the testnet, making it possible for users and tools to differentiate addresses.

Network Prefix Initial Character
mainnet 0x05 0x44 X
testnet 0x04 0x93 T

It is important to note: an address encoded for mainnet use could still be used on the testnet and vice versa; tools that understand the new format are encouraged to implement protections.

Flags

Adding a flags field allow us to make this format slightly more flexible than it would otherwise be. At this time, only 3 flags are specified, one of which is reserved and may not be used in practice.

Name Value Description
NO_TAG 0x00 The address is untagged and treated as if no tag had been specified. The remaining 8 bytes MUST be zero.
TAG_32 0x01 The address contains a 32-bit tag. The tag is encoded in little-endian form in the next 4 bytes. The remaining 4 bytes MUST be zero.
TAG_64 0x02 The address contains a 64-bit tag. The tag is encoded in little-endian form in the next 8 bytes. This flag is reserved in case 64-bit tags ever become supported.

Caution

Exactly one of NO_TAG, TAG_32 and TAG_64 MUST be set; implementations should mask of any other fields when checking which of these flags is set.

All flag values not explicitly defined are reserved for future extensions and MUST be set to 0. Implementations that encounter flag values that are unknown should return an error and treat the address as invalid.

Encoding Example

Below we present how the classic address rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf would be encoded under this spec to include various destination tags. The tag, if any, is on the left most column. The raw bytes, prior to encoding are shown in the second column. Lastly, we show the resulting base58 encoded address.

Encoding for Main Net

Tag Address Raw Bytes
XVLhHMPHU98es4dbozjVtdWzVrDjtV5fdx1mHp98tDMoQXb 44AA066C988C712815CC37AF71472B7CBBBD4E2A0A000000000000000000
0 XVLhHMPHU98es4dbozjVtdWzVrDjtV8AqEL4xcZj5whKbmc 44AA066C988C712815CC37AF71472B7CBBBD4E2A0A010000000000000000
1 XVLhHMPHU98es4dbozjVtdWzVrDjtV8xvjGQTYPiAx6gwDC 44AA066C988C712815CC37AF71472B7CBBBD4E2A0A010100000000000000
2 XVLhHMPHU98es4dbozjVtdWzVrDjtV8zpDURx7DzBCkrQE7 44AA066C988C712815CC37AF71472B7CBBBD4E2A0A010200000000000000
32 XVLhHMPHU98es4dbozjVtdWzVrDjtVoYiC9UvKfjKar4LJe 44AA066C988C712815CC37AF71472B7CBBBD4E2A0A012000000000000000
276 XVLhHMPHU98es4dbozjVtdWzVrDjtVoKj3MnFGMXEFMnvJV 44AA066C988C712815CC37AF71472B7CBBBD4E2A0A011401000000000000
65591 XVLhHMPHU98es4dbozjVtdWzVrDjtVozpjdhPQVdt3ghaWw 44AA066C988C712815CC37AF71472B7CBBBD4E2A0A013700010000000000
16781933 XVLhHMPHU98es4dbozjVtdWzVrDjtVqrDUk2vDpkTjPsY73 44AA066C988C712815CC37AF71472B7CBBBD4E2A0A016D12000100000000
4294967294 XVLhHMPHU98es4dbozjVtdWzVrDjtV1kAsixQTdMjbWi39u 44AA066C988C712815CC37AF71472B7CBBBD4E2A0A01FEFFFFFF00000000
4294967295 XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8yuPT7y4xaEHi 44AA066C988C712815CC37AF71472B7CBBBD4E2A0A01FFFFFFFF00000000

Encoding for Testnet

Tag Address Raw Bytes
none TVE26TYGhfLC7tQDno7G8dGtxSkYQn49b3qD26PK7FcGSKE 93AA066C988C712815CC37AF71472B7CBBBD4E2A0A000000000000000000
0 TVE26TYGhfLC7tQDno7G8dGtxSkYQnSy8RHqGHoGJ59spi2 93AA066C988C712815CC37AF71472B7CBBBD4E2A0A010000000000000000
1 TVE26TYGhfLC7tQDno7G8dGtxSkYQnSz1uDimDdPYXzSpyw 93AA066C988C712815CC37AF71472B7CBBBD4E2A0A010100000000000000
2 TVE26TYGhfLC7tQDno7G8dGtxSkYQnTryP9tG9TW8GeMBmd 93AA066C988C712815CC37AF71472B7CBBBD4E2A0A010200000000000000
32 TVE26TYGhfLC7tQDno7G8dGtxSkYQnT2oqaCDzMEuCDAj1j 93AA066C988C712815CC37AF71472B7CBBBD4E2A0A012000000000000000
276 TVE26TYGhfLC7tQDno7G8dGtxSkYQnTMgJJYfAbsiPsc6Zg 93AA066C988C712815CC37AF71472B7CBBBD4E2A0A011401000000000000
65591 TVE26TYGhfLC7tQDno7G8dGtxSkYQn7ryu2W6njw7mT1jmS 93AA066C988C712815CC37AF71472B7CBBBD4E2A0A013700010000000000
16781933 TVE26TYGhfLC7tQDno7G8dGtxSkYQnVsw45sDtGHhLi27Qa 93AA066C988C712815CC37AF71472B7CBBBD4E2A0A016D12000100000000
4294967294 TVE26TYGhfLC7tQDno7G8dGtxSkYQnX8tDFQ53itLNqs6vU 93AA066C988C712815CC37AF71472B7CBBBD4E2A0A01FEFFFFFF00000000
4294967295 TVE26TYGhfLC7tQDno7G8dGtxSkYQnXoy6kSDh6rZzApc69 93AA066C988C712815CC37AF71472B7CBBBD4E2A0A01FFFFFFFF00000000

Advantages

  • Both the account ID and the destination tag are protected by the 32-bit checksum performed by the Base58Check format.
  • We can determine whether the address is packed by casual visual examination of the address, as well by examining the first byte of the decoded data.
  • Any given pair of tagged addresses referencing the same underlying address but with different destination tags will share a long common prefix when encoded.
  • The address is visually distinct from any addresses already in use.

Disadvantages

The primary disadvantage is the untagged address may appear in several places, including things such as trust lines, responses from the rippled API endpoints and more and that can be confusing for users.

Transaction Example

Consider the following transaction submission:

rippled submit secret '{
    "TransactionType":"Payment",
    "Account":"r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV",
    "Amount":"200000000",
    "Destination":"XVLhHMPHU98es4dbozjVtdWzVrDjtV8zpDURx7DzBCkrQE7"
}'

The server would unpack XVLhHMPHU98es4dbozjVtdWzVrDjtV8zpDURx7DzBCkrQE7 to rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf and a destination tag of 2, and process the submission as if it had been:

rippled submit secret '{
    "TransactionType":"Payment",
    "Account":"r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV",
    "Amount":"200000000",
    "Destination":"rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf",
    "DestinationTag":2
}'

Obviously the server cannot do this when presented with a pre-signed transaction, although assembling and signing a transaction using an unexpanded packed address shouldn't be possible, since the binary encoding of an AccountID requires exactly 20 bytes.

Third party tools that accept user input should allow users to enter such addresses and transparently expand them prior to signing and/or submitting.

Questions

Below is a list of questions to be addressed as a result of this requirements document:

Should the format include both a source and a destination tag?

Probably not. It's better to simply state that the new format specifies an "address and tag" and to allow such addresses to be used both as a source and as a destination, and to decide the tag's type (source or destination) based on the field.

Can we tweak the encoding so that a packed address shares a common prefix with the actual address?

Not trivially. The Base58 format is quirky and doesn't behave the same way as "power-of-two" encodings, like Base32 or Base16. We could have retained a common prefix by encoding the address and destination tag separately and fusing the two components, but that significantly complicated the decoding process, which is something we sought to avoid.

There's potential for confusion. Can we help?

Users may not understand the semantics of a packed address sufficiently. This means that errors or confusion are possible as a result. Two exchange users might wonder why their deposit addresses are now different. Or one user may ask a friend what the deposit address is and, unknowingly, deposit funds into their friend's account. The first is obviously not a problem with existing addresses, and the second is less likely to be an issue than these single addresses. The UX of third parties that choose to use the new format will have to make it clear to users that the address is unique for them.

The server could add information to the metadata associated with a transaction to help tools to map addresses. For example, the server could add the following:

"TaggedAddresses": [
    { 
        "XVLhHMPHU98es4dbozjVtdWzVrDjtV8zpDURx7DzBCkrQE7": {
            "Address": "rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf",
            "Tag": 2
        }
    }
]

Why not BECH32?

The BECH32 format is great and has several advantages over Base58, including speed. We are defining a new address encoding here, so why not just go for that? It's an option, but we decided to be conservative and not require new code to be written or incorporated into servers, clients and libraries.

What happens if someone specifies one of these addresses in the binary format?

The short answer is that they can't. The serialization field only allows exactly 20 bytes, so only the account itself can fit in. This format is only for base58 encoded addresses and is purely a convenience for users.

Should we integrate this natively into the protocol?

No. It would be a huge change and, potentially, breaking one and there aren't any advantages that I can see. There are many risks. Allowing APIs to understand the new format sufficiently to decompose it into its two constituent fields should be sufficient.