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
Allow future extensibility of transaction types by making
TransactionEnvelope
contain a union.
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.
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.
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;
};
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.
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.
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;
};
None yet.