CAP: 0001
Title: Bump Sequence
Author: Nicolas Barry, David Mazieres, Jeremy Rubin
Status: Final
Created: 2018-02-08
Discussion: https://github.com/stellar/stellar-protocol/issues/53
Protocol version: 10
The Bump Sequence operation allows you to bump forward the sequence number of the source account of the operation.
If the specified bumpTo sequence number is greater than the source account's sequence number, the account's sequence number is updated with that value, otherwise it's not modified.
The Bump sequence operation allows you to bump forward the sequence number of the source account of the operation.
Addresses a need when dealing with a large number of pre-signed transactions where there is a need to invalidate an execution branch. The problem is solved by allowing the branches of pre-signed transactions to have non-overlapping sequence numbers and creating a new operation to change the sequence number of the account to an arbitrary number.
The Bump Sequence operation allows you to bump forward the sequence number of the source account of the operation.
If the specified bumpTo
sequence number is greater than the source account's
sequence number, the account's sequence number is updated with that value,
otherwise it's not modified.
Threshold is "Low", in line with the weight required by a signer for the source account to update the sequence number for all transactions.
Note:
This operation only allows bumping the sequence number up to
(current_ledger_number<<32) - 1
.
BumpSequenceOp
specification:
struct BumpSequenceOp
{
SequenceNumber bumpTo;
};
/******* BumpSequence Result ********/
enum BumpSequenceResultCode
{
// codes considered as "success" for the operation
BUMP_SEQUENCE_SUCCESS = 0,
// codes considered as "failure" for the operation
BUMP_SEQUENCE_BAD_SEQ = -1 // `bumpTo` is not within bounds
};
union BumpSequenceResult switch (BumpSequenceResultCode code)
{
case BUMP_SEQUENCE_SUCCESS:
void;
default:
void;
};
New error code for AccountMerge
:
/******* AccountMerge Result ********/
enum AccountMergeResultCode
{
// codes considered as "success" for the operation
ACCOUNT_MERGE_SUCCESS = 0,
// codes considered as "failure" for the operation
ACCOUNT_MERGE_MALFORMED = -1, // can't merge onto itself
ACCOUNT_MERGE_NO_ACCOUNT = -2, // destination does not exist
ACCOUNT_MERGE_IMMUTABLE_SET = -3, // source account has AUTH_IMMUTABLE set
ACCOUNT_MERGE_HAS_SUB_ENTRIES = -4, // account has trust lines/offers
ACCOUNT_MERGE_SEQNUM_TOO_FAR = -5 // sequence number is over max allowed
};
New error code at the operation level:
enum OperationResultCode
{
opINNER = 0, // inner object result is valid
opBAD_AUTH = -1, // too few valid signatures / wrong network
opNO_ACCOUNT = -2, // source account was not found
opNOT_SUPPORTED = -3 // operation not supported at this time
};
Updated meta data format: Transactions now update the sequence number right before applying their operations.
struct TransactionMetaV1
{
LedgerEntryChanges txChanges; // tx level changes if any
OperationMeta operations<>; // meta for each operation
};
// this is the meta produced when applying transactions
// it does not include pre-apply updates such as fees
union TransactionMeta switch (int v)
{
case 0:
OperationMeta operations<>;
case 1:
TransactionMetaV1 v1;
};
Finally this change also switches SequenceNumber
to be a signed integer:
typedef int64 SequenceNumber;
BumpSequenceOp
is a new operation, in order to properly reject the use of this
new operation from nodes that understand the xdr but with the network still
running an older protocol version, a new return value is added to operation
result to communicate the failure. Note that this error code should never
appear in results processed post consensus as operations failing that way
are deemed "invalid".
In addition, to avoid sequence number reuse by re-creating an account that was merged, AccountMerge is modified to not allow merging the account if it would open the possibility of the account being re-created with a smaller sequence number.
The move to signed integers for SequenceNumber
was driven by the same rationale
than for why amounts are modeled as signed integers and the fact that BumpSeqOp
makes it a lot likier to have sequence numbers in the high 64 bit range:
many languages and systems (SQL) do not support unsigned integers properly which
may lead to crashes or unexpected behaviors.
Currently a transaction set is processed in two phases:
- fees and sequence numbers are collected globally
- transactions (and their operations) are applied
The problem with this approach is that with the introduction of an operation
like BumpSequenceOp
, we have some inconsistencies in the way transactions
are processed:
if a transaction bumps the sequence number of an account used in a later transaction, the second transaction is expected to fail but with the current implementation, it won't (as sequence number checks are performed while collecting fees).
One the reasons that BumpSequenceOp
is introduced is to invalidate ranges
of transactions, and with the current behavior it would make it difficult to
reason about the correctness of sequence of transactions.
Only process fees first:
- fees are collected globally as they are now
- transactions are applied, before applying individual transactions:
- numbers are checked for validity
- if the sequence number was valid, update the account's sequence number
We would not change the logic to construct or validate a transaction set: a transaction set would still be built with transactions that have consecutive sequence numbers.
The difference is that in the event that a transaction is invalidated by an
operation from a different transaction, it would fail (collecting fees), which
allows for transactions that make use of BumpSequenceOp
to be able to make
clean assumptions for any operations scheduled for after the bump.
This change will require a new meta data format that can represent transaction level changes when applying the transaction and its operations.
As the sequence number is updated as part of the regular processing of operations,
it requires to emit transaction level meta data, which only existed as part of
the fee processing (included in the txfeehistory
table).
The implementation will switch to using the new meta format for all transactions,
including the ones for older versions of the protocol (which will not emit any
transaction level meta data as expected).
While this is not strictly necessary, it's safer as it makes it obvious that
downstream systems (such as Horizon) need to be updated to support the
new meta data format; the alternative would have been to change the meta data
format with the protocol version, but as protocol version upgrades are not
necessarily managed by the node operators it ends up being less safe.
The shift to signed sequence numbers is backward compatible from a data point of view as the top 32 bits of sequence numbers are seeded with the legder sequence number in older versions of the protocol. Some SDKs may have to be slightly updated if they enforce strong typing. In addition, existing code even with the updated range will continue to work as sequence numbers are within a subset of the range that was supported before.
BumpSequenceOp
rejected on older versions of the protocol- bumps to a small number passed the current sequence number
- bumps to
MAX_INT64
, at which point the account cannot be used as transaction source account - bumps to a number smaller than the current sequence number (should no op)
- bumps to a sequence number that is negative
- don't allow merge when the account sequence number is too high
Corresponding commit is git: 7767d3ef44e9e409fb17dd68f62c276bc0086726