Skip to content

Update "surge pricing" to be more fair/limit based on operations #75

@MonsieurNicolas

Description

@MonsieurNicolas

I think that the implementation to enforce a maximum number of transactions predated the addition of operations.

How this works today:
the "surge pricing" logic kicks in when a validator encounters more transaction candidates than maxTxPerLedger (defined in the ledger header). The first pass at filtering is done by sorting transaction candidates by fee ratio (defined as fee/min_fee where min_fee is base_fee*nb_operations).
The idea is that in a situation where too many transactions are pending, we prioritize the ones with higher average fees.
If fees are equal, the sourceID of the transactions is used as a tie breaker.

Proposal for new behavior:

  1. build a transaction set that maximizes the number of operations in a ledger, up to the maximum maxOperationsPerLedger defined by the network
  2. sort transactions using the average fee per operation as the way to compare transaction fees (done simply by comparing left.fee * right.nb_ops with right.fee * left.nb_ops)
  3. break ties using a deterministic yet randomizing scheme, giving priority to different accounts for each ledger. Using sourceID XOR hash_ofPreviousLedger seems to fit the bill.

In order to implement 1 and 2, the candidate transaction set is build as follows:

  1. trim the transaction set such that it only includes valid transactions (including sequence number and fee)
  2. perform a lexicographical sort the candidate transactions by fee (descending), xored source ID (ascending), sequence number (ascending)
  3. iterate over that sorted list:
    1. if the transaction can fit in the candidate set (ie, newTxSetOpCount <= maxOperationsPerLedger), add it to the set
    2. otherwise, skip the transaction and any other transactions from the same source account

Something else to discuss here is what to do with the ledger header.
Right now the XDR is:

struct LedgerHeader
{
    uint32 ledgerVersion;    // the protocol version of the ledger
    Hash previousLedgerHash; // hash of the previous ledger header
    StellarValue scpValue;   // what consensus agreed to
    Hash txSetResultHash;    // the TransactionResultSet that led to this ledger
    Hash bucketListHash;     // hash of the ledger state

    uint32 ledgerSeq; // sequence number of this ledger

    int64 totalCoins; // total number of stroops in existence.
                      // 10,000,000 stroops in 1 XLM

    int64 feePool;       // fees burned since last inflation run
    uint32 inflationSeq; // inflation sequence number

    uint64 idPool; // last used global ID, used for generating objects

    uint32 baseFee;     // base fee per operation in stroops
    uint32 baseReserve; // account base reserve in stroops

    uint32 maxTxSetSize; // maximum size a transaction set can be

    Hash skipList[4]; // hashes of ledgers in the past. allows you to jump back
                      // in time without walking the chain back ledger by ledger
                      // each slot contains the oldest ledger that is mod of
                      // either 50  5000  50000 or 500000 depending on index
                      // skipList[0] mod(50), skipList[1] mod(5000), etc

    // reserved for future use
    union switch (int v)
    {
    case 0:
        void;
    }
    ext;
};

in particular the line

    uint32 maxTxSetSize; // maximum size a transaction set can be

There are two options on how we would change the ledger header:

  1. change the name of maxTxSetSize to be more generic, something like txSetSize, that maps to "number of transactions" in older versions of the protocol and "number of operations" in newer versions of the protocol (binary format stays the same).
  2. make the ext union support v = 1, we would add the new field
    // reserved for future use
    union switch (int v)
    {
    case 0:
        void;
    case 1:
        uint32 maxOperationsPerLedger;
    }
    ext;

maxTxSize would be set to numeric_limits<uint32>::max()

I prefer option 1 as it keeps the ledger header tidy.
There are very few consumers of this field, and for the most part only use it for reporting. Code that actually cares about it can, just like core's implementation, use it based on what the protocol version is.

This issue was first opened as a core issue stellar/stellar-core#1030

Metadata

Metadata

Labels

CAPRepresents an issue that requires a CAP.

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions