Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
111 lines (82 sloc) 7.69 KB

Preamble

CAP: 0018
Title: Fine-Grained Control of Authorization
Author: Jonathan Jove
Status: Draft
Created: 2019-01-31
Discussion: https://github.com/stellar/stellar-protocol/issues/146
Protocol version: TBD

Simple Summary

This proposal provides issuers with a level of authorization between unauthorized and fully authorized. This level of authorization will allow a trustline to maintain liabilities (see CAP-0003) without permitting any other operations.

Abstract

This proposal adds a new flag to TrustLineFlags which offers a level of authorization intermediate between unauthorized and fully authorized. It is then shown how this new flag enables similar behavior to CAP-0016 by carefully setting and toggling the level of authorization. Additional flags could also be added, which would enable fine-grained control of authorization on a per-account basis.

Motivation

A number of Stellar asset issuers need greater control over assets than simply authorizing particular accounts to hold the asset. The TrustLineEntry.flags field allows an issuer to toggle between the follow two states:

  • TrustLineEntry.flags == 0: The account can hold a balance but cannot receive payments, send payments, maintain offers, or manage offers
  • TrustLineEntry.flags == AUTHORIZED_FLAG: The account can hold a balance, receive payments, send payments, maintain offers, and manage offers

If the issuer does not intend to allow the account to maintain offers, then toggling between the two states described above can be used to control what accounts do with their assets. To achieve this, an issuer could leave accounts in the unauthorized state. In order to do anything with the assets an account holds, that account would need to become authorized. This would require the signature of the issuer. The owner of such an account could then request that the issuer sign a transaction; the issuer should refuse to sign any transaction that does not return the account to the unauthorized state. A transaction that an issuer might be willing to sign could look like

  • Operation 1: Issuer uses AllowTrust to fully authorize account A, asset X
  • Operation 2: Issuer uses AllowTrust to fully authorize account B, asset X
  • Operation 3: Payment from A to B
  • Operation 4: Issuer uses AllowTrust to unauthorize account B, asset X
  • Operation 5: Issuer uses AllowTrust to unauthorize account A, asset X

which would require the signatures of the issuer and account A.

But this approach does not work if the account should be allowed to maintain offers. As soon as the account becomes unauthorized, all of its offers will be removed from the ledger. So some issuers have taken a more drastic approach, where the issuer is actually a signer on the asset holders' accounts. This solution has several drawbacks:

  1. The owner of an account cannot unilaterally utilize assets unrelated to the issuer without the signature of that issuer
  2. If multiple issuers want to become signers on a single account, then many signatures are potentially required for simple operations
  3. Issuers cannot easily rotate keys, because their keys are on every account holding their asset

Specification

This proposal requires the addition of AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG to TrustLineFlags, so the XDR becomes

enum TrustLineFlags
{
    AUTHORIZED_FLAG = 1,                        // issuer has authorized account to perform transactions with its credit
    AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG = 2 // issuer has authorized account to maintain and reduce liabilities for its credit
};

// mask for all trustline flags
const MASK_TRUSTLINE_FLAGS = 3;

In order to interact with these flags, the type of the authorize field of AllowTrustOp must be changed from bool to uint32. This provides binary compatibility with clients that do not know about the new flags (since AUTHORIZED_FLAG == TRUE in XDR), but having a uint32 provides the additional option of setting it to any combination of TrustLineFlags. The XDR becomes

struct AllowTrustOp
{
    AccountID trustor;
    union switch (AssetType type)
    {
    // ASSET_TYPE_NATIVE is not allowed
    case ASSET_TYPE_CREDIT_ALPHANUM4:
        opaque assetCode4[4];

    case ASSET_TYPE_CREDIT_ALPHANUM12:
        opaque assetCode12[12];

        // add other asset types here in the future
    }
    asset;

    // 0, or any bitwise combination of TrustLineFlags
    uint32 authorize;
};

The new behavior of ALLOW_TRUST is to set flags = authorize on the relevant trustline. Semantically, this is equivalent to the existing behavior of ALLOW_TRUST because

  • authorize = FALSE becomes authorize = 0 which removes any authorization from the trustline
  • authorize = TRUE becomes authorize = AUTHORIZED_FLAG which makes the trustline fully authorized

The combination AUTHORIZED_FLAG | AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG is not valid because AUTHORIZED_FLAG implies AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG.

Rationale

Using AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG enables issuers to control what accounts do with their assets even if the accounts should be allowed to maintain offers. To achieve this, an issuer could leave accounts in the AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG state. Such an account is allowed to maintain liabilities, meaning it can own offers but cannot otherwise do anything with the asset. In order to do anything with the assets an account holds, that account would need to become authorized. This would require the signature of the issuer. The owner of such an account could then request that the issuer sign a transaction; the issuer should refuse to sign any transaction that does not return the account to the AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG state. A transaction that an issuer might be willing to sign could look like

  • Operation 1: Issuer uses AllowTrust to fully authorize account A, asset X
  • Operation 2: Issuer uses AllowTrust to fully authorize account B, asset X
  • Operation 3: Payment from A to B
  • Operation 4: Issuer uses AllowTrust to set account B, asset X to AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG state
  • Operation 5: Issuer uses AllowTrust to set account A, asset X to AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG state

or

  • Operation 1: Issuer uses AllowTrust to fully authorize account A, asset X
  • Operation 2: Account A manages offer to buy or sell X
  • Operation 3: Issuer uses AllowTrust to set account A, asset X to AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG state

both of which would require the signatures of the issuer and account A.

In comparison to CAP-0016 this proposal would prohibit a fully authorized account from sending to an account in the AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG state without the signature of the issuer, which could be a desirable feature because it means issuers entirely control their asset. But this solution is flexible in that it would be possible to add other flags to TrustLineFlags which would control other aspects of authorization. For example, one could add the flag AUTHORIZED_TO_INCREASE_BALANCE_FLAG which would permit an account to receive payments. The behavior of CAP-0016 could be emulated entirely with this additional flag. An appropriate set of such flags would provide issuers with fine-grained control of authorization on a per-account basis, which justifies the name of this proposal.

Backwards Compatibility

The only backwards incompatibility with this proposal is if downstream systems relied on the fact that !(TrustLineEntry.flags & AUTHORIZED_FLAG) implies unauthorized. In this case, downstream systems might erroneously conclude that an account is unauthorized when it is in fact in the AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG state.

Test Cases

None yet.

Implementation

None yet.