Skip to content

Instantly share code, notes, and snippets.

@abuiles
Last active April 25, 2020 14:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save abuiles/89bdc491d5217db50c811894dbc4eeb4 to your computer and use it in GitHub Desktop.
Save abuiles/89bdc491d5217db50c811894dbc4eeb4 to your computer and use it in GitHub Desktop.
Protocol 13 support

Protocol 13 support

Stellar Protocol 13 is coming soon, and we need your help bringing support for it to your SDK.

The most relevant changes for SDKs are:

This issue includes all the necessary changes to support Protocol 13, and gives you a path to release a new version of the SDK which is backwards compatible with the current version of the protocol (12). We suggest you aim to release such a backwards compatible version to allow seamless operation through the protocol vote.

For reference, SDF expects to update the testnet to Protocol 13 on 7 May 2020, and pubnet approximately a month after that.

1 - Update XDR

The first step to support Protocol 13 is to recreate your XDR definitions. You can use xdrgen to do that.

You can find the latest XDR definitions in the stellar-core repo.

2 - Update code which depends on TransactionEnvelope

The low-level representation for TransactionEnvelope changed from an xdr.Struct to an xdr.Union. That means you'll need to get the discriminant before reading its value.

/* A TransactionEnvelope wraps a transaction with signatures. */
union TransactionEnvelope switch (EnvelopeType type)
{
case ENVELOPE_TYPE_TX_V0:
    TransactionV0Envelope v0;
case ENVELOPE_TYPE_TX:
    TransactionV1Envelope v1;
case ENVELOPE_TYPE_TX_FEE_BUMP:
    FeeBumpTransactionEnvelope feeBump;
};

As listed above, a transaction envelope can contain a TransactionV0Envelope, a TransactionV1Envelope, or a FeeBumpTransactionEnvelope.

TransactionV0Envelope and TransactionV1Envelope are very similar, but differ in one attribute: the source account.

Next, let's explore how to handle each discriminant. We'll use js-stellar-base as a reference; however, implementation might change depending on your programming language.

TransactionV0Envelope and TransactionV1Envelope

Both v0 and v1 can be handled in the same wrapper class; we extended the Transaction class in js-stellar-base to do so. https://github.com/stellar/js-stellar-base/blob/a15fc74c1f5d37e1a2a94bd610990ccea62292bb/src/transaction.js

In the constructor, we check for the discriminant and throw an error if the envelope doesn't contain a v0 or v1 transaction.

const envelopeType = envelope.switch();
if (
  !(
    envelopeType === xdr.EnvelopeType.envelopeTypeTxV0() ||
    envelopeType === xdr.EnvelopeType.envelopeTypeTx()
  )
) {
  throw new Error(
    `Invalid TransactionEnvelope: expected an envelopeTypeTxV0 or envelopeTypeTx but received an ${envelopeType.name}.`
  );
}

Something important to keep in mind here is that the source account is called sourceAccount of type MuxedAccount in V1, but it is called sourceAccountEd25519 of type Uint256 in V0.

The following code handles both scenarios to pull out their string key representation:

switch (this._envelopeType) {
   case xdr.EnvelopeType.envelopeTypeTxV0():
     this._source = StrKey.encodeEd25519PublicKey(
       this.tx.sourceAccountEd25519()
     );
     break;
   default:
     this._source = StrKey.encodeMuxedAccount(
       this.tx.sourceAccount().toXDR()
     );
     break;
}

Source

Another important thing to keep in mind is that you need to do some special handling when getting the signature base for a V0 transaction.

signatureBase() {
  let tx = this.tx;
  // Backwards Compatibility: Use ENVELOPE_TYPE_TX to sign ENVELOPE_TYPE_TX_V0
  // we need a Transaction to generate the signature base
  if (this._envelopeType === xdr.EnvelopeType.envelopeTypeTxV0()) {
    tx = xdr.Transaction.fromXDR(
      Buffer.concat([
        // TransactionV0 is a transaction with the AccountID discriminant
        // stripped off, we need to put it back to build a valid transaction
        // which we can use to build a TransactionSignaturePayloadTaggedTransaction
        xdr.PublicKeyType.publicKeyTypeEd25519().toXDR(),
        tx.toXDR()
      ])
    );
  }
  const taggedTransaction = new xdr.TransactionSignaturePayloadTaggedTransaction.envelopeTypeTx(
    tx
  );
  const txSignature = new xdr.TransactionSignaturePayload({
    networkId: xdr.Hash.fromXDR(hash(this.networkPassphrase)),
    taggedTransaction
  });
  return txSignature.toXDR();
}

Source

And finally, you also need to add special handling when converting the transaction back to an xdr.TransactionEnvelope (see the implementation of toEnvelope).

For your SDK to be compatible with Protocol 12 and Protocol 13, you need to create a TransactionEnvelope with TransactionV0. If you try to submit a v1 transaction or a fee bump transaction to an instance of Stellar running Protocol 12, then it will fail.

In js-stellar-base we have a builder class which generates v0 transactions by default. You can see the implementation here. You'll also notice that we added a feature flag which allows generation of v1 transactions.

Ideally, once Protocol 13 is released, you should update your SDK to generate V1 transactions by default.

FeeBumpTransactionEnvelope

To handle the scenario when the envelope contains a FeeBumpTransactionEnvelope, let's use js-stellar-base as a reference.

You can see that we added a new class called FeeBumpTransaction, which wraps a xdr.TransactionEnvelope with a Feebump transaction https://github.com/stellar/js-stellar-base/blob/a15fc74c1f5d37e1a2a94bd610990ccea62292bb/src/fee_bump_transaction.js

In the constructor for the class above, you can see the following code:

 const envelopeType = envelope.switch();
 if (envelopeType !== xdr.EnvelopeType.envelopeTypeTxFeeBump()) {
   throw new Error(
     `Invalid TransactionEnvelope: expected an envelopeTypeTxFeeBump but received an ${envelopeType.name}.`
   );
 }

It restricts the kind of transaction envelope which can be received by this class.

You can also see that it has a getter for the innerTransaction, which returns a Transaction class that allows consumers to query data about the transaction being wrapped by the fee bump transaction. This allows you to answer questions like:

  • What was the initial fee that was going to be paid for this transaction? ftx.innerTransaction.fee
  • What is the hash for the innerTransaction? ftx.innerTransaction.hash
  • Who are the signers for the innerTransaction? ftx.innerTransaction.signatures

Besides that, it exposes other fee bump-specific attributes like feeSource, hash, signatureBase, etc.

We also extended the builder to make it easy for users to create fee bump transactions. The helper is called buildFeeBumpTransaction.

In addition, we added a fromXDR helper function to the builder to make it easier to read a Transaction or FeeBumpTransaction. See the implementation here.

3 - Handle muxed accounts

There are two major changes around muxed accounts. The first is updating fields which expect a MuxedAccount; the second is extending Strkey to generated M... accounts.

First, you need to update the following fields — which were previously an xdr.AccountID and are now an xdr.MuxedAccount:

  • PaymentOp.destination
  • PathPaymentStrictReceiveOp.destination
  • PathPaymentStrictSendOp.destination
  • Operation.sourceAccount
  • Operation.destination (for ACCOUNT_MERGE)
  • Transaction.sourceAccount
  • FeeBumpTransaction.feeSource

The following pull requests show you the changes in the JS library:

Second, you need to implement SEP0023, which allows you to turn a MuxedAccount into a string. A reference implementation can be found in pull request #330.

4 - Add support for Fine-Grained Control of Authorization

CAP 18 changed the data type for the attribute authorize in AllowTrustOp. It is now a uint32 instead of a boolean. It also includes a new TrustLineFlag which is authorizedToMaintainLiabilitiesFlag with value 2.

You can see the implementation in js-stellar-base here, which still accepts booleans but converts the value to an uint32.

5 - Update Horizon response for Transaction, Balance, Operation, and Effect.

In the Horizon 1.1.0 release we added new attributes to different resources:

  • The following attributes are now included in the transaction resource:
    • fee_account (the account which paid the transaction fee)
    • fee_bump_transaction (only present in Protocol 13 fee bump transactions)
    • inner_transaction (only present in Protocol 13 fee bump transactions) (#2406).
  • Add support for CAP0018: Fine-Grained Control of Authorization (Protocol 13) (#2423).
    • Add is_authorized_to_maintain_liabilities to Balance.
      "balances": [
        {
          "is_authorized": true,
          "is_authorized_to_maintain_liabilities": true,
          "balance": "27.1374422",
          "limit": "922337203685.4775807",
          "buying_liabilities": "0.0000000",
          "selling_liabilities": "0.0000000",
          "last_modified_ledger": 28893780,
          "asset_type": "credit_alphanum4",
          "asset_code": "USD",
          "asset_issuer": "GBSTRUSD7IRX73RQZBL3RQUH6KS3O4NYFY3QCALDLZD77XMZOPWAVTUK"
        },
        {
          "balance": "1.5000000",
          "buying_liabilities": "0.0000000",
          "selling_liabilities": "0.0000000",
          "asset_type": "native"
        }
      ]
      
    • Add authorize_to_maintain_liabilities to AllowTrust operation.
      {
        "id": "124042211741474817",
        "paging_token": "124042211741474817",
        "transaction_successful": true,
        "source_account": "GBSTRUSD7IRX73RQZBL3RQUH6KS3O4NYFY3QCALDLZD77XMZOPWAVTUK",
        "type": "allow_trust",
        "type_i": 7,
        "created_at": "2020-03-27T03:40:10Z",
        "transaction_hash": "a77d4ee5346d55fb8026cdcdad6e4b5e0c440c96b4627e3727f4ccfa6d199e94",
        "asset_type": "credit_alphanum4",
        "asset_code": "USD",
        "asset_issuer": "GBSTRUSD7IRX73RQZBL3RQUH6KS3O4NYFY3QCALDLZD77XMZOPWAVTUK",
        "trustee": "GBSTRUSD7IRX73RQZBL3RQUH6KS3O4NYFY3QCALDLZD77XMZOPWAVTUK",
        "trustor": "GA332TXN6BX2DYKGYB7FW5BWV2JLQKERNX4T7EUJT4MHWOW2TSGC2SPM",
        "authorize": true,
        "authorize_to_maintain_liabilities": true,
      }
      
    • Add effect trustline_authorized_to_maintain_liabilities.
      {
        "id": "0124042211741474817-0000000001",
        "paging_token": "124042211741474817-1",
        "account": "GBSTRUSD7IRX73RQZBL3RQUH6KS3O4NYFY3QCALDLZD77XMZOPWAVTUK",
        "type": "trustline_authorized_to_maintain_liabilities",
        "type_i": 25,
        "created_at": "2020-03-27T03:40:10Z",
        "trustor": "GA332TXN6BX2DYKGYB7FW5BWV2JLQKERNX4T7EUJT4MHWOW2TSGC2SPM",
        "asset_type": "credit_alphanum4",
        "asset_code": "USD"
      }    
      

In the upcoming Horizon 1.2.0 release a base 64 encoding of the bytes in a transaction memo will be included in the Horizon transaction response as memo_bytes

Horizon 1.2.0 also schedule a couple of breaking changes which are required to support CAP-15.

  • The type for the following attributes will be changed from int64 to string in 1.3.0:

You SDK should be able to parse either a string or a number.

6 - Testing

Once you have your implementation ready, you can test it out by submitting a transaction to Horizon 1.1 or later. Remember that for now, your library should produce V0 transactions.

The Stellar Laboratory now has support for protocol 13, so you can also create V1 and fee bump transactions and test them out in the XDR viewer there.

References stellar/stellar-protocol#600.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment