{"action":"created","issue":{"url":"https://api.github.com/repos/xrp-community/standards-drafts/issues/6","repository_url":"https://api.github.com/repos/xrp-community/standards-drafts","labels_url":"https://api.github.com/repos/xrp-community/standards-drafts/issues/6/labels{/name}","comments_url":"https://api.github.com/repos/xrp-community/standards-drafts/issues/6/comments","events_url":"https://api.github.com/repos/xrp-community/standards-drafts/issues/6/events","html_url":"https://github.com/xrp-community/standards-drafts/issues/6","id":451749186,"node_id":"MDU6SXNzdWU0NTE3NDkxODY=","number":6,"title":"XLS-5d Standard for Tagged Addresses","user":{"login":"nbougalis","id":5085415,"node_id":"MDQ6VXNlcjUwODU0MTU=","avatar_url":"https://avatars3.githubusercontent.com/u/5085415?v=4","gravatar_id":"","url":"https://api.github.com/users/nbougalis","html_url":"https://github.com/nbougalis","followers_url":"https://api.github.com/users/nbougalis/followers","following_url":"https://api.github.com/users/nbougalis/following{/other_user}","gists_url":"https://api.github.com/users/nbougalis/gists{/gist_id}","starred_url":"https://api.github.com/users/nbougalis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nbougalis/subscriptions","organizations_url":"https://api.github.com/users/nbougalis/orgs","repos_url":"https://api.github.com/users/nbougalis/repos","events_url":"https://api.github.com/users/nbougalis/events{/privacy}","received_events_url":"https://api.github.com/users/nbougalis/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":19,"created_at":"2019-06-04T00:54:22Z","updated_at":"2019-07-15T03:04:16Z","closed_at":null,"author_association":"NONE","body":"<pre>\r\n  XRP Ledger Proposed Standard #5\r\n\r\n  Title:        Tagged Addresses\r\n  Author:       Nikolaos D. Bougalis <nikb@bougalis.net>\r\n  Affiliation:  Ripple\r\n  Created:      2019-05-15\r\n</pre>\r\n\r\n# Abstract\r\n\r\nDestination 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.\r\n\r\nThis 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.\r\n\r\n# Motivation\r\n\r\nAlthough flexible, destination tags suffer from several drawbacks.\r\n\r\nCommunicating a destination tag to users can be a problem for a number of reasons:\r\n\r\n* In the absence of a standard format to represent an (address, tag) pair, different users of destination tags communicate the information in different ways.\r\n* 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\").\r\n* 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.\r\n* 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.\r\n\r\nThis proposal seeks to address this problem by defining a standard format to represent an (address, tag) which:\r\n\r\n* 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.\r\n* Eliminates the need for a separate \"Destination Tag\" field by allowing a single format to represent addresses both with and without tags.\r\n* Includes a built-in checksum as a form of error-checking to reduce the probability that a typographical error will generate a correct address.\r\n\r\n# Limitations\r\n\r\nWe 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. \r\n\r\n## Philosophy\r\n\r\n>“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.”\r\n>\r\n>_— Satoshi Nakamoto_\r\n\r\nWhile 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.\r\n\r\nNot 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.\r\n\r\n# Options\r\n\r\nAlthough 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.\r\n\r\nGiven this constrain, we need to use the Base58Check encoding, leaving us with two options:\r\n\r\n1. A \"tightly packed\" format, where the address and tag are encoded using Base58Check as a single unit.\r\n2. A \"loosely packed\" format, where the address is encoded using Base58Check and the destination tag is encoded separately and appended to the address.\r\n\r\nThe 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.\r\n\r\nThe disadvantage of the \"loose\" format is that a tagged address is more complex to detect, encode and decode.\r\n\r\nOn 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.\r\n\r\n## Status\r\nThis 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.\r\n\r\nComments, criticisms, suggestions and improvements are welcome!\r\n\r\n# Proposal\r\n\r\n## Addresses and Destination Tags\r\n\r\nFor a better introduction to addresses and tags, it may help to reference [https://developers.ripple.com/accounts.html](https://developers.ripple.com/accounts.html#addresses). \r\n\r\n## Existing Format\r\nCurrently, an address is generated by base58-encoding (with a checksum) a 21 byte buffer:\r\n\r\n`[← 1 byte prefix →|← 160 bits of account ID →]`\r\n\r\nThe chosen byte prefix is `TokenType::AccountID` (value 0)\r\n\r\n## Proposed format\r\n\r\nIn 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 the same byte prefix (`TokenType::AccountID`) as a regular address:\r\n\r\n`[← 1 byte prefix →|← 160 bits of account ID →|← 8 bits of flags →|← 64 bits of tag →]`\r\n\r\nThe tag shall always be encoded as a little endian two's complement 64 bit integer.\r\n\r\n### Flags\r\n\r\nAdding 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.\r\n\r\n| Name     | Value  | Description |\r\n|----------|--------|-------------|\r\n| `NO_TAG` | `0x00` | The address is untagged and treated as if no tag had been specified. The remaining 8 bytes **MUST** be zero. |\r\n| `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. |\r\n| `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. |\r\n\r\n#### Caution\r\n\r\nExactly 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.\r\n\r\nAll 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.\r\n\r\n### Encoding Example\r\n\r\nIn the following table, 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, followed by the base58 encoded address. We also show the raw bytes, grouped into three sections separated by spaces: the account ID, the flags, and the tag bytes. We do not include the token type which is always 0 and is stripped out during the decoding process.\r\n\r\n| Tag | Address | Raw Bytes |\r\n|----------|------------------------------------------------|--------------------------------------------------------------|\r\n|None      |`r1WTvVjuoBM9vsm2p395AyzCQcJyEp8aG4YHcqE3XLDehK`|`AA066C988C712815CC37AF71472B7CBBBD4E2A0A 00 0000000000000000`|\r\n|0         |`r1WTvVjuoBM9vsm2p395AyzCQcJyEfcHYq9yDEBxVaZPrv`|`AA066C988C712815CC37AF71472B7CBBBD4E2A0A 01 0000000000000000`|\r\n|1         |`r1WTvVjuoBM9vsm2p395AyzCQcJyEfdPpmB79nKUGeeWSD`|`AA066C988C712815CC37AF71472B7CBBBD4E2A0A 01 0100000000000000`|\r\n|2         |`r1WTvVjuoBM9vsm2p395AyzCQcJyEfe7W6UyhuSSs9B3c2`|`AA066C988C712815CC37AF71472B7CBBBD4E2A0A 01 0200000000000000`|\r\n|32        |`r1WTvVjuoBM9vsm2p395AyzCQcJyE3DJxdyvuoBZWXPCNi`|`AA066C988C712815CC37AF71472B7CBBBD4E2A0A 01 2000000000000000`|\r\n|276       |`r1WTvVjuoBM9vsm2p395AyzCQcJyEfzBRqiF1pyW3aoKhN`|`AA066C988C712815CC37AF71472B7CBBBD4E2A0A 01 1401000000000000`|\r\n|65591     |`r1WTvVjuoBM9vsm2p395AyzCQcJyE3eVsfqQrBa3X4q4qF`|`AA066C988C712815CC37AF71472B7CBBBD4E2A0A 01 3700010000000000`|\r\n|16781933  |`r1WTvVjuoBM9vsm2p395AyzCQcJyE9CCWEMvo5U5sP5Px5`|`AA066C988C712815CC37AF71472B7CBBBD4E2A0A 01 6D12000100000000`|\r\n|4294967294|`r1WTvVjuoBM9vsm2p395AyzCQcJyEURPMMjRhJoyxQhdt5`|`AA066C988C712815CC37AF71472B7CBBBD4E2A0A 01 FEFFFFFF00000000`|\r\n|4294967295|`r1WTvVjuoBM9vsm2p395AyzCQcJyEUS7qHkuz3AD8zhAbQ`|`AA066C988C712815CC37AF71472B7CBBBD4E2A0A 01 FFFFFFFF00000000`|\r\n\r\n\r\n## Advantages\r\n* Both the account ID and the destination tag are protected by the 32-bit checksum performed by the Base58Check format.\r\n* We can determine whether the address is packed by examining the size of the decoded data: if the size is 29 bytes it is a packed address which may contain a tag; if the size is 20 bytes it does not. if the size is anything else, the address is malformed.\r\n\r\n## Disadvantages\r\nThe primary disadvantage is that the packed address will not share a common prefix with an unpacked address, which can be confusing for users.\r\n\r\nIn the above example the common prefix shared by all packed addresses was the 29-character long string `r1WTvVjuoBM9vsm2p395AyzCQcJyE`. That shares no commonalities with the \"classic\" encoding of the address: `rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf`.\r\n\r\nThis discrepancy can be especially confusing for users in the context of trust lines, where the issuer of an asset must be explicitly trusted.\r\n\r\n# Transaction Example\r\n\r\nConsider the following transaction submission:\r\n\r\n```\r\nrippled submit secret '{\r\n    \"TransactionType\":\"Payment\",\r\n    \"Account\":\"r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV\",\r\n    \"Amount\":\"200000000\",\r\n    \"Destination\":\"r1WTvVjuoBM9vsm2p395AyzCQcJyEfzBRqiF1pyW3aoKhN\"\r\n}'\r\n```\r\n\r\nThe server would unpack `r1WTvVjuoBM9vsm2p395AyzCQcJyEfzBRqiF1pyW3aoKhN` to `rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf` and a destination tag of 276, and process the submission as if it had been:\r\n\r\n```\r\nrippled submit secret '{\r\n    \"TransactionType\":\"Payment\",\r\n    \"Account\":\"r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV\",\r\n    \"Amount\":\"200000000\",\r\n    \"Destination\":\"rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf\",\r\n    \"DestinationTag\":276\r\n}'\r\n```\r\n\r\nObviously 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. \r\n\r\nThird party tools that accept user input should allow users to enter such addresses and transparently expand them prior to signing and/or submitting.\r\n\r\n\r\n\r\n## Questions\r\n\r\nBelow is a list of questions to be addressed as a result of this requirements document:\r\n\r\n#### Should the format include both a source and a destination tag?\r\nProbably  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.\r\n\r\n#### Can we tweak the encoding so that a packed address shares a common prefix with the actual address?\r\nNot 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.\r\n\r\n#### There's potential for confusion. Can we help?\r\nUsers  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.\r\nThe 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.\r\n\r\nThe 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:\r\n\r\n```\r\n\"TaggedAddresses\": [\r\n    { \r\n        \"r1WTvVjuoBM9vsm2p395AyzCQcJyEfzBRqiF1pyW3aoKhN\": {\r\n            \"Address\": \"rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf\",\r\n            \"Tag\": 276\r\n        }\r\n    }\r\n]\r\n```\r\n\r\n#### Why not BECH32?\r\nThe [BECH32](https://en.bitcoin.it/wiki/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.\r\n\r\n#### What happens if someone specifies one of these addresses in the binary format?\r\nThe  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.\r\n\r\n#### Should we integrate this natively into the protocol?\r\nNo. 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.\r\n\r\n## Sample C++ Implementation\r\n\r\nThe following is an example of a C++ function which will encode an address in a tag in a way that complies with this specification:\r\n\r\n```\r\n#include <array>\r\n#include <cstdint>\r\n#include <string>\r\n\r\n#include <boost/endian/conversion.hpp>\r\n#include <boost/optional.hpp>\r\n\r\n#include <ripple/protocol/AccountID.h>\r\n\r\nstd::string\r\ntoBase58(AccountID const& v, boost::optional<std::uint32_t> tag)\r\n{\r\n    std::array<std::uint8_t, 29> buf;\r\n\r\n    auto iter = std::copy(v.begin(), v.end(), buf.begin());\r\n    \r\n    if (!tag)\r\n    {\r\n        std::fill(iter, buf.end(), 0);\r\n        return base58EncodeToken(TokenType::AccountID, buf.data(), buf.size());\r\n    }\r\n    \r\n    // We treat all tags as 64-bit, internally:\r\n    std::uint64_t t = boost::endian::native_to_little(static_cast<std::uint64_t>(*tag));\r\n\r\n    // We are encoding a 32-bit tag, even if it's stored in 64 bits:\r\n    *iter++ = 0x01;\r\n    \r\n    // Copy the tag bytes\r\n    std::copy_n(reinterpret_cast<std::uint8_t const*>(std::addressof(t)), sizeof(t), iter);\r\n    \r\n    return base58EncodeToken(TokenType::AccountID, buf.data(), buf.size());\r\n}\r\n```\r\n"},"comment":{"url":"https://api.github.com/repos/xrp-community/standards-drafts/issues/comments/511262227","html_url":"https://github.com/xrp-community/standards-drafts/issues/6#issuecomment-511262227","issue_url":"https://api.github.com/repos/xrp-community/standards-drafts/issues/6","id":511262227,"node_id":"MDEyOklzc3VlQ29tbWVudDUxMTI2MjIyNw==","user":{"login":"sublimator","id":525211,"node_id":"MDQ6VXNlcjUyNTIxMQ==","avatar_url":"https://avatars1.githubusercontent.com/u/525211?v=4","gravatar_id":"","url":"https://api.github.com/users/sublimator","html_url":"https://github.com/sublimator","followers_url":"https://api.github.com/users/sublimator/followers","following_url":"https://api.github.com/users/sublimator/following{/other_user}","gists_url":"https://api.github.com/users/sublimator/gists{/gist_id}","starred_url":"https://api.github.com/users/sublimator/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sublimator/subscriptions","organizations_url":"https://api.github.com/users/sublimator/orgs","repos_url":"https://api.github.com/users/sublimator/repos","events_url":"https://api.github.com/users/sublimator/events{/privacy}","received_events_url":"https://api.github.com/users/sublimator/received_events","type":"User","site_admin":false},"created_at":"2019-07-15T03:04:16Z","updated_at":"2019-07-15T03:04:16Z","author_association":"NONE","body":"@nbougalis\r\n\r\nDoesn't seem to be much progress here? \r\n\r\nMy \"proposal\" was mostly just some fun, but I regret even putting it out there, as it's just a spanner in the works. I got another email from some poor soul that lost money. Interesting to note that they didn't realize the destination tag changed per transaction at the particular gateway.\r\n\r\nThis is still a real problem. "}}
{"actor":{"display_login":"sublimator"}}