Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
168 lines (143 sloc) 4.82 KB

Preamble

CAP: 0019
Title: Future-upgradable TransactionEnvelope type
Author: David Mazières
Status: Accepted
Created: 2019-03-07
Discussion: https://groups.google.com/forum/#!forum/stellar-dev
Protocol version: TBD

Simple Summary

Allow future extensibility of transaction types by making TransactionEnvelope contain a union.

Abstract

TransactionEnvelope now contains a union for the transaction type. For binary compatibility, legacy transactions are now of type Transaction0 and can have an Ed25519 account ID only.

Motivation

Right now, it will be very difficult to upgrade the Transaction type in a backwards compatible way. However, until we add a new account type, we can take advantage of the fact that all Transaction structures, when marshaled, start with a 0-valued 32-bit integer (part of the AccountID which can only be a PublicKey or type PUBLIC_KEY_TYPE_ED25519). We make TransactionEnvelope contain a union, and when the discriminant is 0, we have it contain a new type Transaction0 which is basically a Transaction with the PUBLIC_KEY_TYPE_ED25519 stripped off the beginning and the key type hard-coded to Ed25519.

Specification

We need only two changes. EnvelopeType conveniently didn't have a 0, so we now allocate that for legacy transaction envelopes. We then add a union inside TransactionEnvelope. Note that TransactionSignaturePayload already had a union, so we don't need to change it. In particularly, we don't add a case for ENVELOPE_TYPE_TX0 because we don't want there to be multiple ways to compute a transaction ID.

We expect implementations to provide a helper function for converting a Transaction0 into a Transaction, as doing so is necessary for computing the transaction ID.

enum EnvelopeType
{
    ENVELOPE_TYPE_TX0 = 0,  // new
    ENVELOPE_TYPE_SCP = 1,
    ENVELOPE_TYPE_TX = 2,
    ENVELOPE_TYPE_AUTH = 3
};

struct Transaction0
{
    uint256 sourceAccountEd25519;  // was: AccountID sourceAccount;
    uint32 fee;
    SequenceNumber seqNum;
    TimeBounds* timeBounds;
    Memo memo;
    Operation operations<100>;
    union switch (int v) {
    case 0:
        void;
    } ext;
};

/* A TransactionEnvelope wraps a transaction with signatures. */
union TransactionEnvelope switch (EnvelopeType type) {
case ENVELOPE_TYPE_TX0:
    struct {
        Transaction0 tx;
        /* Each decorated signature is a signature over the SHA256 hash of
         * a TransactionSignaturePayload */
        DecoratedSignature signatures<20>;
    } v0;
case ENVELOPE_TYPE_TX:
    struct {
        Transaction tx;
        /* Each decorated signature is a signature over the SHA256 hash of
         * a TransactionSignaturePayload */
        DecoratedSignature signatures<20>;
    } v1;
};

Rationale

There have been a number of proposals to change the transaction format, or add different types of transaction. We have also discussed new AccountID types. Though we haven't decided on which proposals to adopt, changing the transaction format will become much harder if we do so after adopting a new AccountID type, so we might as well make this union change now.

Backwards Compatibility

The changes are backwards compatible with legacy binary transactions. The new code will be able to read old transactions, but old code cannot read the new TransactionEnvelope type. Hence, a phased deployment makes sense.

Example

Imagine we want to add a new feeSource field to transactions. We can simply introduce a new type Transaction4 that contains this extra field, then:

enum EnvelopeType
{
    ENVELOPE_TYPE_TX0 = 0,
    ENVELOPE_TYPE_SCP = 1,
    ENVELOPE_TYPE_TX = 2,
    ENVELOPE_TYPE_AUTH = 3,
    ENVELOPE_TYPE_TX4 = 4  // new
};

struct TransactionSignaturePayload
{
    Hash networkId;
    union switch (EnvelopeType type)
    {
    case ENVELOPE_TYPE_TX:
        Transaction tx;
        /* All other values of type are invalid */
    case ENVELOPE_TYPE_TX4:
        Transaction4 tx4;
    }
    taggedTransaction;
};

/* A TransactionEnvelope wraps a transaction with signatures. */
union TransactionEnvelope switch (EnvelopeType type) {
case ENVELOPE_TYPE_TX0:
    struct {
        Transaction0 tx;
        /* Each decorated signature is a signature over the SHA256 hash of
         * a TransactionSignaturePayload */
        DecoratedSignature signatures<20>;
    } v0;
case ENVELOPE_TYPE_TX:
    struct {
        Transaction tx;
        /* Each decorated signature is a signature over the SHA256 hash of
         * a TransactionSignaturePayload */
        DecoratedSignature signatures<20>;
    } v1;
case ENVELOPE_TYPE_TX4:
    struct {
        Transaction4 tx;
        /* Each decorated signature is a signature over the SHA256 hash of
         * a TransactionSignaturePayload */
        DecoratedSignature signatures<20>;
    } v4;
};

Implementation

None yet.

You can’t perform that action at this time.