Skip to content

Cosigned assets: NopOp and COAUTHORIZED_FLAG #146

Closed
@tomerweller

Description

(Posting on behalf of David Mazières)

Motivation

A number of Stellar asset issuers need greater control over assets
than simply authorizing particular accounts to hold the asset. As a
result, we are seeing people build solutions in which the asset issuer
is actually a co-signer on asset holders' accounts. This solution has
several drawbacks. In particular, the asset owner cannot unilaterally
withdraw XLM or other assets from his or her account, and it is very
hard to trade one such restricted asset for another (as both issuers
would need to be cosigners on the account, giving one issuer
permission to authorize transactions on the other's asset).

This proposal is a first cut at attempting to address the problem
through minimal stellar-core changes.

Co-signer authorized trust lines

To accommodate asset issuers with restrictions, we propose adding
another mode for trustlines of AUTH_REQUIRED assets, called
"cosigner authorized," that lies somewhere between being not
authorized and fully authorized. The idea is to allow issuers to
require asset holders to request co-signing of transactions involving
the asset without being cosigners on asset holders' accounts.

enum TrustLineFlags
{
    // issuer has authorized account to perform transactions with its credit
    AUTHORIZED_FLAG = 1,
    // issuer allows transactions that are co-signed by issuer low threshold
    COAUTHORIZED_FLAG = 2
};

There are now three modes for the trustline of an AUTH_REQUIRED
asset, based on TrustLineEntry::flags:

  • If (flags & 3) == 0, the source account is not authorized. Any
    PAYMENT, PATH_PAYMENT, MANAGE_OFFER, or
    CREATE_PASSIVE_OFFER, with the source account causes the whole
    transaction to fail, with only two exceptions:

    • A PATH_PAYMENT is allowed to contain the asset in path, so
      long as it does not apear in sendAsset or destAsset, and

    • A MANAGE_OFFER is allowed if it sets amount to 0 (to delete
      the offer).

    In addition, any operation with a different (even authorized) source
    account will fail if it attempts to send the asset to an account
    with (flags & 3) == 0. Finally, existing orders on the order book
    belonging to the account with (flags & 3) == 0 are immediately
    invalid and cannot be filled. This behavior should be close or
    identical to the status quo.

  • If (flags & 3) == COAUTHORIZED_FLAG for an account, then the
    account has certain additional permissions compared to when those
    bits are 0. Specifically:

    • The account's offers in the order book continue to be valid, and
      can be filled at any time.

    • The account can use MANAGE_OFFER to reduce the amount of an
      offer involving the asset (including canceling the offer with
      and amount of 0), but not to increase the offer.

    • The account can receive (but not send) payments and path
      payments in the asset, up to the limit of the trustline.

    However, any other operation on the asset requires that the
    transaction be co-signed by the asset issuer at low threshold.
    Moreover, if the account increases the limit on the trustline and
    the transaction is not cosigned by the issuer, then the
    COAUTHORIZED_FLAG is cleared so (flags & 3) goes back to 0.

  • If (flags & 3) == AUTHORIZED_FLAG, then the account can perform
    arbitrary transactions on the asset, including sending payments to
    accounts with COAUTHORIZED_FLAG.

Operation changes

Implementing cosigned assets requires changes to two operations.
Because an asset issuer's signature may be out of place on a
transaction requiring asset issuer coauthorization, we add a NopOp
operation that requires a signature from an arbitrary source account.
Such an operation will be useful in other contexts, too, as currently
pre-authorized transactions sometimes require ugly hacks like having
an account send one Stroup to itself. Second, we have to modify
ALLOW_TRUST to enable issuers to set the new COAUTHORIZED_FLAG.

NO_OPERATION (NopOp)

The NO_OPERATION operation does nothing, but requires a valid
signature from the operation's source account. Since the default
source account is the source of the transaction, this will always
succeed at low threshold if the operation's sourceAccount is
NULL. However, it can be used to require a low threshold signature
from an asset issuer.

enum NopOp {
    LOW_THRESHOLD = 0,
    MED_THRESHOLD = 1,
    HIGH_THRESHOLD = 2
};

enum NopResult {
    NOP_SUCCESS = 0,
    NOP_BAD_THRESHOLD = -1,  // the operation was an invalid value
};

enum OperationType
{
    ...
    BUMP_SEQUENCE = 11,
    NO_OPERATION = 12
};

struct Operation
{
    union switch (OperationType type)
    {
    case NO_OPERATION:
        NopOp nopOp;
    }
    body;
};

union OperationResult switch (OperationResultCode code)
{
case opINNER:
    union switch (OperationType type)
    {
        ...
    case NO_OPERATION:
        NopResult nopResult;
    }
    tr;
default:
    void;
};

NopOp is not strictly necessary, as a redundant ALLOW_TRUST can be
used instead, but having NopOp is cleaner and has other applications
to pre-authorized transactions.

ALLOW_TRUST flags

Only one small change is required to AllowTrustOp. We change the
authorize field from a bool to a uint32. This provides binary
compatibility with clients that do not know about the
COAUTHORIZED_FLAG (since AUTHORIZED_FLAG == TRUE in XDR), but
having a uint32 provides the additional option of setting it to
COAUTHORIZED_FLAG.

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, AUTHORIZED_FLAG, or COAUTHORIZED_FLAG
    uint32 authorize;
};

Metadata

Assignees

No one assigned

    Labels

    CAPRepresents an issue that requires a CAP.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions