Skip to content

Latest commit

 

History

History
350 lines (291 loc) · 10.7 KB

cap-0032.md

File metadata and controls

350 lines (291 loc) · 10.7 KB

Preamble

CAP: 0032
Title: Trustline Preauthorization
Author: Jonathan Jove
Status: Draft
Created: 2020-04-28
Discussion: https://github.com/stellar/stellar-protocol/issues/303
Protocol version: TBD

Simple Summary

This proposal makes it possible to authorize a trust line before it is created.

Motivation

This proposal seeks to solve the following problem: an issuer should be able to authorize a trust line without waiting for the trust line to be created.

Consider, for example, an issuer that wants to issue AUTH_REQUIRED assets to an account that does not have a trust line for the asset. With the current version of the protocol, the issuer will need to wait until the account has created a trust line before authorizing the trust line. This presents friction to the issuer, which must now employ additional machinery to either monitor the ledger or use pre-signed/pre-authorized transactions. In either case, complexity is increased for an operation fundamental to the network.

Goals Alignment

This proposal is aligned with several Stellar Network Goals, among them:

  • The Stellar Network should make it easy for developers of Stellar projects to create highly usable products.
  • The Stellar Network should enable cross-border payments, i.e. payments via exchange of assets, throughout the globe, enabling users to make payments between assets in a manner that is fast, cheap, and highly usable.
    • In support of this, the Stellar Network should enable asset issuance, but as a means of enabling cross-border payments.

Abstract

We introduce PreauthorizationEntry as a new type of LedgerEntry which represents authorization to hold an asset, potentially before the corresponding trust line has been created. The operation CreatePreauthorizationOp makes it possible to create a PreauthorizationEntry whereas the operation RemovePreauthorizationOp makes it possible to remove a PreauthorizationEntry. When creating a trust line with ChangeTrustOp, the flags will now be set from a corresponding PreauthorizationEntry if it exists. AllowTrustOp will also be updated to modify just a trust line, just a PreauthorizationEntry, or both depending on which exist.

Specification

XDR

Refactor AllowTrustOp

union NonNativeAssetCode switch (AssetType type)
{
// ASSET_TYPE_NATIVE is not allowed
case ASSET_TYPE_CREDIT_ALPHANUM4:
    AssetCode4 assetCode4;
case ASSET_TYPE_CREDIT_ALPHANUM12:
    AssetCode12 assetCode12;
};

struct AllowTrustOp
{
    AccountID trustor;

    NonNativeAssetCode asset;

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

PreauthorizationEntry

enum LedgerEntryType
{
    // ... ACCOUNT, TRUSTLINE, OFFER, unchanged ...
    DATA = 3,
    PREAUTHORIZATION = 4
};

struct PreauthorizationEntry
{
    // Account for which the preauthorization applies
    AccountID accountID;

    // Asset for which the preauthorization applies
    Asset asset;

    // See TrustLineFlags
    uint32 flags;

    // Amount of native asset to pay the reserve
    int64 reserve;

    // reserved for future use
    union switch (int v)
    {
    case 0:
        void;
    }
    ext;
};

struct LedgerEntry
{
    uint32 lastModifiedLedgerSeq; // ledger the LedgerEntry was last changed

    union switch (LedgerEntryType type)
    {
    // ... ACCOUNT, TRUSTLINE, OFFER, DATA unchanged ...
    case PREAUTHORIZATION:
        PreauthorizationEntry preauthorization;
    }
    data;

    // reserved for future use
    union switch (int v)
    {
    case 0:
        void;
    }
    ext;
};

struct LedgerKey
{
    // ... ACCOUNT, TRUSTLINE, OFFER, DATA unchanged ...
case PREAUTHORIZATION:
    struct
    {
        AccountID accountID;
        Asset asset;
    } preauthorization;
};

Operations

enum OperationType
{
    // ... CREATE_ACCOUNT, ..., MANAGE_BUY_OFFER unchanged ...
    PATH_PAYMENT_STRICT_SEND = 13,
    CREATE_PREAUTHORIZATION = 14,
    REMOVE_PREAUTHORIZATION = 15
};

struct CreatePreauthorizationOp
{
    // Account to preauthorize
    AccountID accountID;

    // Preauthorization asset
    NonNativeAssetCode asset;
};

struct RemovePreauthorizationOp
{
    // Account from which to remove preauthorization
    AccountID accountID;

    // Preauthorization asset
    NonNativeAssetCode asset;
};

struct Operation
{
    // sourceAccount is the account used to run the operation
    // if not set, the runtime defaults to "sourceAccount" specified at
    // the transaction level
    AccountID* sourceAccount;

    union switch (OperationType type)
    {
    // ... CREATE_ACOUNT, ..., PATH_PAYMENT_STRICT_SEND unchanged ...
    case CREATE_PREAUTHORIZATION:
        CreatePreauthorizationOp createPreauthorizationOp;
    case REMOVE_PREAUTHORIZATION:
        RemovePreauthorizationOp removePreauthorizationOp;
    }
    body;
};

Operation Results

enum CreatePreauthorizationResultCode
{
    CREATE_PREAUTHORIZATION_SUCCESS = 0,
    CREATE_PREAUTHORIZATION_MALFORMED = -1,
    CREATE_PREAUTHORIZATION_ALREADY_EXISTS = -2,
    CREATE_PREAUTHORIZATION_LOW_RESERVE = -3
};

union CreatePreauthorizationResult switch (CreatePreauthorizationResultCode code)
{
case CREATE_PREAUTHORIZATION_SUCCESS:
    void;
default:
    void;
};

enum RemovePreauthorizationResultCode
{
    REMOVE_PREAUTHORIZATION_SUCCESS = 0,
    REMOVE_PREAUTHORIZATION_DOES_NOT_EXIST = -1,
    REMOVE_PREAUTHORIZATION_LINE_FULL = -2
};

union RemovePreauthorizationResult switch (RemovePreauthorizationResultCode code)
{
case REMOVE_PREAUTHORIZATION_SUCCESS:
    void;
default:
    void;
};

struct OperationResult
{
    // sourceAccount is the account used to run the operation
    // if not set, the runtime defaults to "sourceAccount" specified at
    // the transaction level
    AccountID* sourceAccount;

    union switch (OperationType type)
    {
    // ... CREATE_ACOUNT, ..., REMOVE_SPONSORSHIP unchanged ...
    case CREATE_PREAUTHORIZATION:
        CreatePreauthorizationResult createPreauthorizationResult;
    case REMOVE_PREAUTHORIZATIOn:
        RemovePreauthorizationResult removePreauthorizationResult;
    }
    body;
};

Semantics

CreatePreauthorizationOp

A PreauthorizationEntry can only be created by the CreatePreauthorizationOp operation. CreatePreauthorizationOp is invalid with CREATE_PREAUTHORIZATION_MALFORMED if asset is invalid.

The behavior of CreatePreauthorizationOp is as follows:

  1. Fail with CREATE_PREAUTHORIZATION_ALREADY_EXISTS if a PreauthorizationEntry with the specified accountID and asset already exists
  2. Fail with CREATE_PREAUTHORIZATION_LOW_RESERVE if the sourceAccount does not have at least baseReserve available balance of native asset
  3. Deduct baseReserve of native asset from sourceAccount
  4. Create a PreauthorizationEntry as preauthorization with the following properties:
    • preauthorization.accountID = accountID
    • preauthorization.asset = asset
    • preauthorization.flags = 0 if sourceAccount.flags & AUTH_REQUIRED_FLAG and preauthorization.flags = AUTHORIZED_FLAG otherwise
  5. If a trust line tl with the specified accountID and asset exists, then set preauthorization.flags = tl.flags
  6. Succeed with CREATE_PREAUTHORIZATION_SUCCESS

CreatePreauthorizationOp requires low threshold because it cannot be used to send funds.

RemovePreauthorizationOp

A PreauthorizationEntry can only be removed by the RemovePreauthorizationOp operation. RemovePreauthorizationOp is invalid with REMOVE_PREAUTHORIZATION_MALFORMED if asset is invalid.

The behavior of RemovePreauthorizationOp is as follows:

  1. Fail with REMOVE_PREAUTHORIZATION_DOES_NOT_EXIST if there does not exist a PreauthorizationEntry with the specified accountID and asset
  2. Fail with REMOVE_PREAUTHORIZATION_LINE_FULL if the sourceAccount does not have at least reserve available limit of native asset
  3. Add reserve of native asset to sourceAccount
  4. Remove the specified PreauthorizationEntry
  5. Succeed with REMOVE_PREAUTHORIZATION_SUCCESS

RemovePreauthorizationOp requires low threshold because it cannot be used to send funds.

ChangeTrustOp

The behavior of ChangeTrustOp is unchanged except when creating a trust line. In this case, we add one additional step immediately before success:

  1. ...
  2. If a preauthorization entry preauthorization with the sourceAccount and specified asset exists, then set flags = preauthorization.flags
  3. Succeed with CHANGE_TRUST_SUCCESS

AllowTrustOp

The behavior of AllowTrustOp must be modified to maintain the invariant that a trust line and a preauthorization entry for the same accountID and asset must have the same flags.

First, we now fail with ALLOW_TRUST_NO_TRUST_LINE only if both the trust line and preauthorization entry do not exist:

  1. ...
  2. Load the trust line and preauthorization entry with the sourceAccount and specified assetID
  3. Fail with ALLOW_TRUST_NO_TRUST_LINE if neither exists
  4. ...

Second, immediately before success we must set the flags on the trust line and the preauthorization entry if they exist:

  1. ...
  2. If the trust line exists with the sourceAccount and specified asset exists, set the trust line flags as specified
  3. If the preauthorization entry with the sourceAccount and specified asset exists, set the preauthorization entry flags as specified
  4. Suceed with ALLOW_TRUST_SUCCESS

Design Rationale

PreauthorizationEntry is not a sub-entry

Each PreauthorizationEntry exists as an independent entity on the ledger. It is clear that a PreauthorizationEntry cannot be a sub-entry of accountID, because it is a security risk for accounts to be able to add sub-entries to other accounts. But why should these entries be independent entities on the ledger rather than sub-entries of the accounts that created them? The main benefit of this design is that issuers are not limited in the number of preauthorization entries they can create.

TrustLines and Preauthorizations Can Exist Simultaneously

This proposal makes it possible for a trust line and a preauthorization entry with the same accountID and asset to exist simultaneously. We avoid any ambiguity in the authorization state by guaranteeing that if both do exist simultaneously, then they must have the same flags. It is possible to maintain this property because there is at most one trust line and one preauthorization entry for a given accountID and asset.

Backwards Incompatibilities

All downstream systems will need updated XDR in order to recognize the new operations and ledger entries.

Security Concerns

This proposal will slightly reduce the efficacy of base reserve changes, because a PreauthorizationEntry that has insufficient reserve is still usable.

Test Cases

None yet.

Implementation

None yet.