CAP: 0027
Title: First-class multiplexed accounts
Author: David Mazières and Tomer Weller
Status: Final
Created: 2019-12-05
Discussion: https://groups.google.com/forum/#!topic/stellar-dev/LIFvbMi9jPo
Protocol version: 13
A new type of Account ID includes a 64-bit memo ID. This memo ID has no effect on the semantics of operations or their authorization, but it facilitates multiplexing a single account across multiple users.
A common pattern in the Stellar ecosystem is for services to share a single Stellar account ID across many users, relying on the memo ID to disambiguate incoming payments.
Experience shows that people frequently forget to include the memo ID, resulting in either lost funds or onerous support calls. Moreover, memo IDs are per transaction, not per occurrence of an account ID, which imposes restrictions on the use of multiplexed accounts. For example, it is not possible to include multiple payments to different multiplexed accounts in the same transaction. Similarly, it is not possible to refund payments from a multiplexed account ID, as the transaction's memo ID by convention describes only the destination, not the source of funds.
By adding an optional memo ID to the account ID type, we make multiplexed accounts a first-class abstraction that can be used anywhere a normal account ID can be used.
First-class multiplexed accounts help scalability by eliminating an incentive for users to create many accounts when virtual accounts would suffice. They also significantly improve usability by addressing the pain point of support costs for users who forget memo IDs.
A new type, MuxedAccount
, replaces AccountID
in many places. A
MuxedAccount
can contain either a plain Ed25519 public key, or a
public key and a 64-bit subaccount ID. The subaccount ID has no
effect on the semantics of transactions, but can be used by
higher-layer software to multiplex a single stellar account among
multiple users.
The multiplexed account type is represented by a new XDR union:
enum CryptoKeyType
{
KEY_TYPE_ED25519 = 0,
KEY_TYPE_MUXED_ED25519 = 256, // This is new
KEY_TYPE_PRE_AUTH_TX = 1,
KEY_TYPE_HASH_X = 2
};
// Source or destination of a payment operation
union MuxedAccount switch (CryptoKeyType type) {
case KEY_TYPE_ED25519:
uint256 ed25519;
case KEY_TYPE_MUXED_ED25519:
struct {
uint64 id;
uint256 ed25519;
} med25519;
};
The following fields, which were previously an AccountID
or
AccountID*
, are now a MuxedAccount
or MuxedAccount*
(respectively):
PaymentOp::destination
PathPaymentStrictReceiveOp::destination
PathPaymentStrictSendOp::destination
Operation::sourceAccount
Operation::destination
(forACCOUNT_MERGE
)Transaction::sourceAccount
FeeBumpTransaction::feeSource
Note, however, that this must not be implemented before CAP-0015 or
CAP-0019 (which updates the transaction format), as these other CAPs
depend on all existing transactions starting with 4 zero bytes (which
will no longer be the case when the transaction's sourceAccount
is a
MuxedAccount
).
A previous ecosystem-only proposal had too many limitations, including the inability to send payments to several virtual accounts, the inability to specify virtual accounts as a source, and increased cognitive load for developers. The CAP approach seems cleaner, with very little added complexity in the server.
The particular set of fields promoted to MuxedAccount
were chosen to
avoid any changes to the database. Hence, assets and offers are still
associated with an AccountID
rather than a MuxedAccount
.
As for the strkey update, there were 3 unused bits at the bottom of
each version byte that do not affect the first character, so we
decided to reserve these to facilitate future crypto agility. A
different letter was chosen for multiplexed accounts because these are
not completely interchangable with traditional AccountID
values.
(For instance, a multiplexed account cannot issue assets, only the
base account can do so.)
All existing transactions will continue to be valid, but transactions
with a MuxedAccount
will not be parsable by old software. Hence,
the best time to roll out this change will be at the same time as
CAP-0015. Software that converts new-style transactions to old can
also strip out the identifiers from MuxedAccount
structures.
Certain addresses that are identical may not look identical. This could confuse client software. For instance, sending an asset to a multiplexed account tied to the issuer will destroy the asset.
Code that uses a denylist to block specific accounts from receiving
AUTH\_REQUIRED
assets will need to ensure that multiplexed
accounts cannot be used to bypass the denylist. This is not an
issue for non-AUTH\_REQUIRED
assets, as anyone can already evade
such denylists by creating new accounts. There is no issue for
allowlists, since failure to accommodate multiplexed accounts just
prohibits people from using them, preserving the status quo ante.
People commonly assume that base-32 decoding libraries will reject inputs with invalid lengths or invalid trailing bits, but this is often not the case, particularly when padding is disabled. If implementations forget to check this, they will parse invalid strkeys. This could lead to problems, particularly if strkeys are used as indexes in a hash table, where an attacker could create multiple entries for the same account.
An implementation can be fetched to FETCH_HEAD
with the following
command:
git fetch [email protected]:xdrpp/stellar-core dm/muxacct
The github web page associated with that branch is here: https://github.com/xdrpp/stellar-core/tree/dm/muxacct