From 931ff0bc4b9e537516db4bf46cf1b1489e908bcc Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Fri, 16 Jan 2026 17:56:51 +0100 Subject: [PATCH 1/9] Implemented and integrated MempoolSorting interface --- .../src/mempool/private/PrivateMempool.ts | 27 ++++++++++++++++-- .../src/mempool/sorting/MempoolSorting.ts | 28 +++++++++++++++++++ .../inmemory/InMemoryTransactionStorage.ts | 24 +++++++++++----- .../repositories/TransactionStorage.ts | 5 +++- 4 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 packages/sequencer/src/mempool/sorting/MempoolSorting.ts diff --git a/packages/sequencer/src/mempool/private/PrivateMempool.ts b/packages/sequencer/src/mempool/private/PrivateMempool.ts index 71469946..448b9bd9 100644 --- a/packages/sequencer/src/mempool/private/PrivateMempool.ts +++ b/packages/sequencer/src/mempool/private/PrivateMempool.ts @@ -12,20 +12,27 @@ import { TransactionValidator } from "../verification/TransactionValidator"; import { Tracer } from "../../logging/Tracer"; import { trace } from "../../logging/trace"; import { IncomingMessagesService } from "../../settlement/messages/IncomingMessagesService"; +import { MempoolSorting } from "../sorting/MempoolSorting"; +import { DefaultMempoolSorting } from "../sorting/DefaultMempoolSorting"; @sequencerModule() export class PrivateMempool extends SequencerModule implements Mempool { public readonly events = new EventEmitter(); + private readonly mempoolSorting: MempoolSorting; + public constructor( private readonly transactionValidator: TransactionValidator, @inject("TransactionStorage") private readonly transactionStorage: TransactionStorage, @inject("IncomingMessagesService", { isOptional: true }) private readonly messageService: IncomingMessagesService | undefined, - @inject("Tracer") public readonly tracer: Tracer + @inject("Tracer") public readonly tracer: Tracer, + @inject("MempoolSorting", { isOptional: true }) + mempoolSorting: MempoolSorting | undefined ) { super(); + this.mempoolSorting = mempoolSorting ?? new DefaultMempoolSorting(); } public async length(): Promise { @@ -36,7 +43,12 @@ export class PrivateMempool extends SequencerModule implements Mempool { public async add(tx: PendingTransaction): Promise { const [txValid, error] = this.transactionValidator.validateTx(tx); if (txValid) { - const success = await this.transactionStorage.pushUserTransaction(tx); + const sortingValue = this.mempoolSorting!.presortingPriority(tx); + + const success = await this.transactionStorage.pushUserTransaction( + tx, + sortingValue + ); if (success) { this.events.emit("mempool-transaction-added", tx); log.trace(`Transaction added to mempool: ${tx.hash().toString()}`); @@ -69,10 +81,19 @@ export class PrivateMempool extends SequencerModule implements Mempool { offset: number, limit?: number ): Promise { - return await this.transactionStorage.getPendingUserTransactions( + const txs = await this.transactionStorage.getPendingUserTransactions( offset, limit ); + + if (this.mempoolSorting.enablePostSorting()) { + // Sorts in place + txs.sort( + this.mempoolSorting.presortingPriority.bind(this.mempoolSorting) + ); + } + + return txs; } @trace("mempool.get_mandatory_txs") diff --git a/packages/sequencer/src/mempool/sorting/MempoolSorting.ts b/packages/sequencer/src/mempool/sorting/MempoolSorting.ts new file mode 100644 index 00000000..9c74eafb --- /dev/null +++ b/packages/sequencer/src/mempool/sorting/MempoolSorting.ts @@ -0,0 +1,28 @@ +import { PendingTransaction } from "../PendingTransaction"; + +export interface MempoolSorting { + /** + * Presorting happens on the backend (i.e. the DB), before the data travels to the sequencer. + * It's very fast, but limited to only integer sorting. + * The value returned here has to be static per transaction, since it will be sorted and + * compared on the DB-side. + * + * @param tx + * @returns Priority of the transaction - larger is better (therefore will be + * put in the block first) + */ + presortingPriority(tx: PendingTransaction): number; + + /** + * Indicate whether to do pre-sorting (as it's expensive depending on your block size) + */ + enablePostSorting(): boolean; + + /** + * Postsorting happens on the sequencer-side. It's less fast but can take in any two + * transactions and directly compare them based on arbitrary logic + * @param a + * @param b + */ + postSorting(a: PendingTransaction, b: PendingTransaction): number; +} diff --git a/packages/sequencer/src/storage/inmemory/InMemoryTransactionStorage.ts b/packages/sequencer/src/storage/inmemory/InMemoryTransactionStorage.ts index 10cadc0c..90732770 100644 --- a/packages/sequencer/src/storage/inmemory/InMemoryTransactionStorage.ts +++ b/packages/sequencer/src/storage/inmemory/InMemoryTransactionStorage.ts @@ -9,7 +9,7 @@ import { InMemoryBatchStorage } from "./InMemoryBatchStorage"; @injectable() export class InMemoryTransactionStorage implements TransactionStorage { - private queue: PendingTransaction[] = []; + private queue: { tx: PendingTransaction; sortingValue: number }[] = []; private latestScannedBlock = -1; @@ -21,12 +21,17 @@ export class InMemoryTransactionStorage implements TransactionStorage { public async removeTx(hashes: string[]) { const hashSet = new Set(hashes); - this.queue = this.queue.filter((tx) => { + this.queue = this.queue.filter(({ tx }) => { const hash = tx.hash().toString(); return !hashSet.has(hash); }); } + private sortQueue() { + // Sort in-place and descending + this.queue.sort(({ sortingValue: a }, { sortingValue: b }) => b - a); + } + public async getPendingUserTransactions( offset: number, limit?: number @@ -42,25 +47,30 @@ export class InMemoryTransactionStorage implements TransactionStorage { if (block !== undefined) { const hashes = block.transactions.map((tx) => tx.tx.hash().toString()); this.queue = this.queue.filter( - (tx) => !hashes.includes(tx.hash().toString()) + ({ tx }) => !hashes.includes(tx.hash().toString()) ); } } this.latestScannedBlock = nextHeight - 1; + this.sortQueue(); + const from = offset ?? 0; const to = limit !== undefined ? from + limit : undefined; - return this.queue.slice(from, to); + return this.queue.slice(from, to).map(({ tx }) => tx); } - public async pushUserTransaction(tx: PendingTransaction): Promise { + public async pushUserTransaction( + tx: PendingTransaction, + priority: number + ): Promise { const notInQueue = this.queue.find( - (tx2) => tx2.hash().toString() === tx.hash().toString() + ({ tx: tx2 }) => tx2.hash().toString() === tx.hash().toString() ) === undefined; if (notInQueue) { - this.queue.push(tx); + this.queue.push({ tx, sortingValue: priority }); } return notInQueue; } diff --git a/packages/sequencer/src/storage/repositories/TransactionStorage.ts b/packages/sequencer/src/storage/repositories/TransactionStorage.ts index 522d63eb..0a8696bc 100644 --- a/packages/sequencer/src/storage/repositories/TransactionStorage.ts +++ b/packages/sequencer/src/storage/repositories/TransactionStorage.ts @@ -1,7 +1,10 @@ import { PendingTransaction } from "../../mempool/PendingTransaction"; export interface TransactionStorage { - pushUserTransaction: (tx: PendingTransaction) => Promise; + pushUserTransaction: ( + tx: PendingTransaction, + priority: number + ) => Promise; getPendingUserTransactions: ( offset: number, From c55f43e0309288b66eaaa232aa291fa6f21051c9 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Fri, 16 Jan 2026 17:57:39 +0100 Subject: [PATCH 2/9] Implemented Default and NonceMempoolSorting --- packages/sequencer/src/index.ts | 3 ++ .../mempool/sorting/DefaultMempoolSorting.ts | 31 +++++++++++++++++++ .../mempool/sorting/NonceMempoolSorting.ts | 30 ++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 packages/sequencer/src/mempool/sorting/DefaultMempoolSorting.ts create mode 100644 packages/sequencer/src/mempool/sorting/NonceMempoolSorting.ts diff --git a/packages/sequencer/src/index.ts b/packages/sequencer/src/index.ts index a0fee017..e4bc3fbd 100644 --- a/packages/sequencer/src/index.ts +++ b/packages/sequencer/src/index.ts @@ -3,6 +3,9 @@ export * from "./mempool/Mempool"; export * from "./mempool/PendingTransaction"; export * from "./mempool/CompressedSignature"; export * from "./mempool/private/PrivateMempool"; +export * from "./mempool/sorting/MempoolSorting"; +export * from "./mempool/sorting/DefaultMempoolSorting"; +export * from "./mempool/sorting/NonceMempoolSorting"; export * from "./sequencer/executor/Sequencer"; export * from "./sequencer/executor/Sequenceable"; export * from "./sequencer/SequencerIdProvider"; diff --git a/packages/sequencer/src/mempool/sorting/DefaultMempoolSorting.ts b/packages/sequencer/src/mempool/sorting/DefaultMempoolSorting.ts new file mode 100644 index 00000000..eb8b2fa3 --- /dev/null +++ b/packages/sequencer/src/mempool/sorting/DefaultMempoolSorting.ts @@ -0,0 +1,31 @@ +import { noop } from "@proto-kit/common"; + +import { PendingTransaction } from "../PendingTransaction"; +import { + SequencerModule, + sequencerModule, +} from "../../sequencer/builder/SequencerModule"; + +import { MempoolSorting } from "./MempoolSorting"; + +@sequencerModule() +export class DefaultMempoolSorting + extends SequencerModule + implements MempoolSorting +{ + public async start() { + noop(); + } + + public enablePostSorting(): boolean { + return false; + } + + public postSorting(a: PendingTransaction, b: PendingTransaction): number { + return 0; + } + + public presortingPriority(tx: PendingTransaction): number { + return 0; + } +} diff --git a/packages/sequencer/src/mempool/sorting/NonceMempoolSorting.ts b/packages/sequencer/src/mempool/sorting/NonceMempoolSorting.ts new file mode 100644 index 00000000..a39a7c7d --- /dev/null +++ b/packages/sequencer/src/mempool/sorting/NonceMempoolSorting.ts @@ -0,0 +1,30 @@ +import { noop } from "@proto-kit/common"; + +import { sequencerModule } from "../../sequencer/builder/SequencerModule"; +import { PendingTransaction } from "../PendingTransaction"; + +import { MempoolSorting } from "./MempoolSorting"; +import { DefaultMempoolSorting } from "./DefaultMempoolSorting"; + +@sequencerModule() +export class NonceMempoolSorting + extends DefaultMempoolSorting + implements MempoolSorting +{ + public enablePostSorting(): boolean { + return true; + } + + public postSorting(a: PendingTransaction, b: PendingTransaction): number { + if (a.sender.equals(b.sender).toBoolean()) { + return Number(a.nonce.toBigInt() - b.nonce.toBigInt()); + } else { + // Return 0, i.e. a should come before b (stuff stays in-order) + return 0; + } + } + + public async start() { + noop(); + } +} From 4aeba4750bdd52c86785545f070f0eb0ae6e332d Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Fri, 16 Jan 2026 17:57:55 +0100 Subject: [PATCH 3/9] Added priority to Prisma transaction storage --- .../migration.sql | 10 +++++++ packages/persistance/prisma/schema.prisma | 12 ++++++++ .../prisma/PrismaTransactionStorage.ts | 29 +++++++++++++++---- 3 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 packages/persistance/prisma/migrations/20260116164904_transaction_priority/migration.sql diff --git a/packages/persistance/prisma/migrations/20260116164904_transaction_priority/migration.sql b/packages/persistance/prisma/migrations/20260116164904_transaction_priority/migration.sql new file mode 100644 index 00000000..2d318924 --- /dev/null +++ b/packages/persistance/prisma/migrations/20260116164904_transaction_priority/migration.sql @@ -0,0 +1,10 @@ +-- CreateTable +CREATE TABLE "TransactionPriority" ( + "transactionHash" TEXT NOT NULL, + "priority" INTEGER NOT NULL, + + CONSTRAINT "TransactionPriority_pkey" PRIMARY KEY ("transactionHash") +); + +-- AddForeignKey +ALTER TABLE "TransactionPriority" ADD CONSTRAINT "TransactionPriority_transactionHash_fkey" FOREIGN KEY ("transactionHash") REFERENCES "Transaction"("hash") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/packages/persistance/prisma/schema.prisma b/packages/persistance/prisma/schema.prisma index d0ed1472..043dedea 100644 --- a/packages/persistance/prisma/schema.prisma +++ b/packages/persistance/prisma/schema.prisma @@ -54,6 +54,18 @@ model Transaction { executionResult TransactionExecutionResult? IncomingMessageBatchTransaction IncomingMessageBatchTransaction[] + + priority TransactionPriority? +} + +model TransactionPriority { + transactionHash String + + priority Int + + Transaction Transaction @relation(fields: [transactionHash], references: [hash]) + + @@id([transactionHash]) } model TransactionExecutionResult { diff --git a/packages/persistance/src/services/prisma/PrismaTransactionStorage.ts b/packages/persistance/src/services/prisma/PrismaTransactionStorage.ts index 49256eef..8f49f038 100644 --- a/packages/persistance/src/services/prisma/PrismaTransactionStorage.ts +++ b/packages/persistance/src/services/prisma/PrismaTransactionStorage.ts @@ -34,6 +34,11 @@ export class PrismaTransactionStorage implements TransactionStorage { equals: false, }, }, + orderBy: { + priority: { + priority: "desc", + }, + }, skip: offset, take: limit, }); @@ -56,13 +61,27 @@ export class PrismaTransactionStorage implements TransactionStorage { } } - public async pushUserTransaction(tx: PendingTransaction): Promise { + public async pushUserTransaction( + tx: PendingTransaction, + priority: number + ): Promise { const { prismaClient } = this.connection; - const result = await prismaClient.transaction.createMany({ - data: [this.transactionMapper.mapOut(tx)], - skipDuplicates: true, - }); + const transactionData = this.transactionMapper.mapOut(tx); + + const [result] = await prismaClient.$transaction([ + prismaClient.transaction.createMany({ + data: [transactionData], + skipDuplicates: true, + }), + + prismaClient.transactionPriority.create({ + data: { + priority, + transactionHash: transactionData.hash, + }, + }), + ]); return result.count === 1; } From fa0f74064b58c55838aabd00116e66c442439905 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Sat, 17 Jan 2026 13:45:19 +0100 Subject: [PATCH 4/9] Removed NonceSorting, reenabled sorting test --- .../indexer/src/tasks/IndexPendingTxTask.ts | 2 +- packages/sequencer/src/index.ts | 1 - .../mempool/sorting/DefaultMempoolSorting.ts | 3 +- .../mempool/sorting/NonceMempoolSorting.ts | 30 --------------- .../{Mempool.test.ts => Block-order.test.ts} | 38 ++++++++++++------- 5 files changed, 28 insertions(+), 46 deletions(-) delete mode 100644 packages/sequencer/src/mempool/sorting/NonceMempoolSorting.ts rename packages/sequencer/test/integration/{Mempool.test.ts => Block-order.test.ts} (88%) diff --git a/packages/indexer/src/tasks/IndexPendingTxTask.ts b/packages/indexer/src/tasks/IndexPendingTxTask.ts index db468660..9d448c6a 100644 --- a/packages/indexer/src/tasks/IndexPendingTxTask.ts +++ b/packages/indexer/src/tasks/IndexPendingTxTask.ts @@ -30,7 +30,7 @@ export class IndexPendingTxTask public async compute(input: PendingTransaction): Promise { try { - await this.transactionStorage.pushUserTransaction(input); + await this.transactionStorage.pushUserTransaction(input, 0); return ""; } catch (err) { log.error("Failed to process pending tx task", err); diff --git a/packages/sequencer/src/index.ts b/packages/sequencer/src/index.ts index e4bc3fbd..5821284b 100644 --- a/packages/sequencer/src/index.ts +++ b/packages/sequencer/src/index.ts @@ -5,7 +5,6 @@ export * from "./mempool/CompressedSignature"; export * from "./mempool/private/PrivateMempool"; export * from "./mempool/sorting/MempoolSorting"; export * from "./mempool/sorting/DefaultMempoolSorting"; -export * from "./mempool/sorting/NonceMempoolSorting"; export * from "./sequencer/executor/Sequencer"; export * from "./sequencer/executor/Sequenceable"; export * from "./sequencer/SequencerIdProvider"; diff --git a/packages/sequencer/src/mempool/sorting/DefaultMempoolSorting.ts b/packages/sequencer/src/mempool/sorting/DefaultMempoolSorting.ts index eb8b2fa3..88b3500f 100644 --- a/packages/sequencer/src/mempool/sorting/DefaultMempoolSorting.ts +++ b/packages/sequencer/src/mempool/sorting/DefaultMempoolSorting.ts @@ -26,6 +26,7 @@ export class DefaultMempoolSorting } public presortingPriority(tx: PendingTransaction): number { - return 0; + // This means we order by first in, first out in the db + return -Date.now(); } } diff --git a/packages/sequencer/src/mempool/sorting/NonceMempoolSorting.ts b/packages/sequencer/src/mempool/sorting/NonceMempoolSorting.ts deleted file mode 100644 index a39a7c7d..00000000 --- a/packages/sequencer/src/mempool/sorting/NonceMempoolSorting.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { noop } from "@proto-kit/common"; - -import { sequencerModule } from "../../sequencer/builder/SequencerModule"; -import { PendingTransaction } from "../PendingTransaction"; - -import { MempoolSorting } from "./MempoolSorting"; -import { DefaultMempoolSorting } from "./DefaultMempoolSorting"; - -@sequencerModule() -export class NonceMempoolSorting - extends DefaultMempoolSorting - implements MempoolSorting -{ - public enablePostSorting(): boolean { - return true; - } - - public postSorting(a: PendingTransaction, b: PendingTransaction): number { - if (a.sender.equals(b.sender).toBoolean()) { - return Number(a.nonce.toBigInt() - b.nonce.toBigInt()); - } else { - // Return 0, i.e. a should come before b (stuff stays in-order) - return 0; - } - } - - public async start() { - noop(); - } -} diff --git a/packages/sequencer/test/integration/Mempool.test.ts b/packages/sequencer/test/integration/Block-order.test.ts similarity index 88% rename from packages/sequencer/test/integration/Mempool.test.ts rename to packages/sequencer/test/integration/Block-order.test.ts index 2b252f78..0f3ee564 100644 --- a/packages/sequencer/test/integration/Mempool.test.ts +++ b/packages/sequencer/test/integration/Block-order.test.ts @@ -1,4 +1,4 @@ -import { log, TypedClass } from "@proto-kit/common"; +import { expectDefined, log, TypedClass } from "@proto-kit/common"; import { Runtime } from "@proto-kit/module"; import { Protocol } from "@proto-kit/protocol"; import { Bool, PrivateKey, UInt64 } from "o1js"; @@ -14,6 +14,7 @@ import { StorageDependencyFactory, VanillaTaskWorkerModules, AppChain, + ManualBlockTrigger, } from "../../src"; import { DefaultTestingSequencerModules, @@ -23,19 +24,21 @@ import { import { Balance } from "./mocks/Balance"; import { createTransaction } from "./utils"; -// TODO Reenable with next PR -describe.skip.each([["InMemory", InMemoryDatabase]])( - "Mempool test", +describe.each([["InMemory", InMemoryDatabase]])( + "Block Ordering test: %s", ( testName, Database: TypedClass ) => { let appChain: ReturnType; let sequencer: Sequencer< - DefaultTestingSequencerModules & { Database: typeof Database } + DefaultTestingSequencerModules & { + Database: typeof Database; + } >; let runtime: Runtime<{ Balance: typeof Balance }>; let mempool: PrivateMempool; + let trigger: ManualBlockTrigger; async function mempoolAddTransactions( userPrivateKey: PrivateKey, @@ -116,6 +119,7 @@ describe.skip.each([["InMemory", InMemoryDatabase]])( sequencer = appChain.sequencer; mempool = sequencer.resolve("Mempool"); + trigger = sequencer.resolve("BlockTrigger"); }); afterEach(async () => { @@ -123,7 +127,7 @@ describe.skip.each([["InMemory", InMemoryDatabase]])( }); it("transactions are returned in right order - simple", async () => { - expect.assertions(13); + expect.assertions(14); await mempoolAddTransactions(user1PrivateKey, 0); await mempoolAddTransactions(user2PrivateKey, 0); @@ -132,7 +136,9 @@ describe.skip.each([["InMemory", InMemoryDatabase]])( await mempoolAddTransactions(user2PrivateKey, 1); await mempoolAddTransactions(user3PrivateKey, 1); - const txs = await mempool.getTxs(0); + const block = await trigger.produceBlock(); + expectDefined(block); + const txs = block.transactions.map((x) => x.tx); expect(txs).toHaveLength(6); expect(txs[0].nonce.toBigInt()).toStrictEqual(0n); @@ -150,7 +156,7 @@ describe.skip.each([["InMemory", InMemoryDatabase]])( }); it("transactions are returned in right order - medium", async () => { - expect.assertions(13); + expect.assertions(14); log.setLevel("TRACE"); @@ -161,7 +167,9 @@ describe.skip.each([["InMemory", InMemoryDatabase]])( await mempoolAddTransactions(user2PrivateKey, 1); await mempoolAddTransactions(user3PrivateKey, 0); - const txs = await mempool.getTxs(0); + const block = await trigger.produceBlock(); + expectDefined(block); + const txs = block.transactions.map((x) => x.tx); expect(txs).toHaveLength(6); expect(txs[0].nonce.toBigInt()).toStrictEqual(0n); @@ -179,7 +187,7 @@ describe.skip.each([["InMemory", InMemoryDatabase]])( }); it("transactions are returned in right order - harder", async () => { - expect.assertions(13); + expect.assertions(14); await mempoolAddTransactions(user1PrivateKey, 0); await mempoolAddTransactions(user2PrivateKey, 1); @@ -188,7 +196,9 @@ describe.skip.each([["InMemory", InMemoryDatabase]])( await mempoolAddTransactions(user3PrivateKey, 0); await mempoolAddTransactions(user1PrivateKey, 1); - const txs = await mempool.getTxs(0); + const block = await trigger.produceBlock(); + expectDefined(block); + const txs = block.transactions.map((x) => x.tx); expect(txs).toHaveLength(6); expect(txs[0].nonce.toBigInt()).toStrictEqual(0n); @@ -206,7 +216,7 @@ describe.skip.each([["InMemory", InMemoryDatabase]])( }); it("transactions are returned in right order - hardest", async () => { - expect.assertions(13); + expect.assertions(14); await mempoolAddTransactions(user1PrivateKey, 0); await mempoolAddTransactions(user1PrivateKey, 4); @@ -217,7 +227,9 @@ describe.skip.each([["InMemory", InMemoryDatabase]])( await mempoolAddTransactions(user3PrivateKey, 0); await mempoolAddTransactions(user1PrivateKey, 1); - const txs = await mempool.getTxs(0); + const block = await trigger.produceBlock(); + expectDefined(block); + const txs = block.transactions.map((x) => x.tx); expect(txs).toHaveLength(6); expect(txs[0].nonce.toBigInt()).toStrictEqual(0n); From b418be8a6384e10a70693a6b5c528a37f9a48f29 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Sat, 17 Jan 2026 13:49:08 +0100 Subject: [PATCH 5/9] Added changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2eb240f..a34052ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Added +- Added Mempool sorting [#395](https://github.com/proto-kit/framework/pull/395) - Introduced block explorer [#381](https://github.com/proto-kit/framework/pull/381) - Added CircuitAnalysisModule for easy analysis of protocol circuits [#379](https://github.com/proto-kit/framework/pull/379) - Separated settlement and bridging functionally, so now settlement can be used without bridging [#376](https://github.com/proto-kit/framework/pull/376) From c73001951ed54fedea27fd3355084d9e60a7b9b8 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Sat, 17 Jan 2026 14:44:23 +0100 Subject: [PATCH 6/9] Changed priority to bigint in DB --- .../migration.sql | 2 +- packages/persistance/prisma/schema.prisma | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename packages/persistance/prisma/migrations/{20260116164904_transaction_priority => 20260117134313_transaction_priority}/migration.sql (92%) diff --git a/packages/persistance/prisma/migrations/20260116164904_transaction_priority/migration.sql b/packages/persistance/prisma/migrations/20260117134313_transaction_priority/migration.sql similarity index 92% rename from packages/persistance/prisma/migrations/20260116164904_transaction_priority/migration.sql rename to packages/persistance/prisma/migrations/20260117134313_transaction_priority/migration.sql index 2d318924..e0475292 100644 --- a/packages/persistance/prisma/migrations/20260116164904_transaction_priority/migration.sql +++ b/packages/persistance/prisma/migrations/20260117134313_transaction_priority/migration.sql @@ -1,7 +1,7 @@ -- CreateTable CREATE TABLE "TransactionPriority" ( "transactionHash" TEXT NOT NULL, - "priority" INTEGER NOT NULL, + "priority" BIGINT NOT NULL, CONSTRAINT "TransactionPriority_pkey" PRIMARY KEY ("transactionHash") ); diff --git a/packages/persistance/prisma/schema.prisma b/packages/persistance/prisma/schema.prisma index 043dedea..a5abc433 100644 --- a/packages/persistance/prisma/schema.prisma +++ b/packages/persistance/prisma/schema.prisma @@ -61,7 +61,7 @@ model Transaction { model TransactionPriority { transactionHash String - priority Int + priority BigInt Transaction Transaction @relation(fields: [transactionHash], references: [hash]) From 3f410d748e81990f98c2f6cff57599ce1976c717 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Sat, 17 Jan 2026 14:52:35 +0100 Subject: [PATCH 7/9] Changed post sorting interface to full array --- packages/sequencer/src/mempool/private/PrivateMempool.ts | 7 ++----- .../sequencer/src/mempool/sorting/DefaultMempoolSorting.ts | 6 +++--- packages/sequencer/src/mempool/sorting/MempoolSorting.ts | 4 +--- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/packages/sequencer/src/mempool/private/PrivateMempool.ts b/packages/sequencer/src/mempool/private/PrivateMempool.ts index e79cd4c7..5300c50e 100644 --- a/packages/sequencer/src/mempool/private/PrivateMempool.ts +++ b/packages/sequencer/src/mempool/private/PrivateMempool.ts @@ -81,16 +81,13 @@ export class PrivateMempool extends SequencerModule implements Mempool { offset?: number, limit?: number ): Promise { - const txs = await this.transactionStorage.getPendingUserTransactions( + let txs = await this.transactionStorage.getPendingUserTransactions( offset ?? 0, limit ); if (this.mempoolSorting.enablePostSorting()) { - // Sorts in place - txs.sort( - this.mempoolSorting.presortingPriority.bind(this.mempoolSorting) - ); + txs = this.mempoolSorting.postSorting(txs); } return txs; diff --git a/packages/sequencer/src/mempool/sorting/DefaultMempoolSorting.ts b/packages/sequencer/src/mempool/sorting/DefaultMempoolSorting.ts index 88b3500f..d8666ad7 100644 --- a/packages/sequencer/src/mempool/sorting/DefaultMempoolSorting.ts +++ b/packages/sequencer/src/mempool/sorting/DefaultMempoolSorting.ts @@ -21,12 +21,12 @@ export class DefaultMempoolSorting return false; } - public postSorting(a: PendingTransaction, b: PendingTransaction): number { - return 0; + public postSorting(transactions: PendingTransaction[]): PendingTransaction[] { + return transactions; } public presortingPriority(tx: PendingTransaction): number { // This means we order by first in, first out in the db - return -Date.now(); + return Date.UTC(2500, 0) - Date.now(); } } diff --git a/packages/sequencer/src/mempool/sorting/MempoolSorting.ts b/packages/sequencer/src/mempool/sorting/MempoolSorting.ts index 9c74eafb..fde5370e 100644 --- a/packages/sequencer/src/mempool/sorting/MempoolSorting.ts +++ b/packages/sequencer/src/mempool/sorting/MempoolSorting.ts @@ -21,8 +21,6 @@ export interface MempoolSorting { /** * Postsorting happens on the sequencer-side. It's less fast but can take in any two * transactions and directly compare them based on arbitrary logic - * @param a - * @param b */ - postSorting(a: PendingTransaction, b: PendingTransaction): number; + postSorting(transactions: PendingTransaction[]): PendingTransaction[]; } From 06825661a83d78467e11b282c745fc1c2262422e Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Sat, 17 Jan 2026 15:25:26 +0100 Subject: [PATCH 8/9] Added mempool type configuration --- .../PrismaBlockProduction.test.ts | 2 +- .../src/mempool/private/PrivateMempool.ts | 20 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/persistance/test-integration/PrismaBlockProduction.test.ts b/packages/persistance/test-integration/PrismaBlockProduction.test.ts index f6299f6b..e9d33515 100644 --- a/packages/persistance/test-integration/PrismaBlockProduction.test.ts +++ b/packages/persistance/test-integration/PrismaBlockProduction.test.ts @@ -251,7 +251,7 @@ describe("prisma integration", () => { PrismaTransactionStorage ); - const txs = await txResolver.getPendingUserTransactions(); + const txs = await txResolver.getPendingUserTransactions(0); expectDefined(transaction.transaction); diff --git a/packages/sequencer/src/mempool/private/PrivateMempool.ts b/packages/sequencer/src/mempool/private/PrivateMempool.ts index 5300c50e..c2e3c4e1 100644 --- a/packages/sequencer/src/mempool/private/PrivateMempool.ts +++ b/packages/sequencer/src/mempool/private/PrivateMempool.ts @@ -15,8 +15,15 @@ import { IncomingMessagesService } from "../../settlement/messages/IncomingMessa import { MempoolSorting } from "../sorting/MempoolSorting"; import { DefaultMempoolSorting } from "../sorting/DefaultMempoolSorting"; +type PrivateMempoolConfig = { + type?: "hybrid" | "private" | "based"; +}; + @sequencerModule() -export class PrivateMempool extends SequencerModule implements Mempool { +export class PrivateMempool + extends SequencerModule + implements Mempool +{ public readonly events = new EventEmitter(); private readonly mempoolSorting: MempoolSorting; @@ -35,6 +42,10 @@ export class PrivateMempool extends SequencerModule implements Mempool { this.mempoolSorting = mempoolSorting ?? new DefaultMempoolSorting(); } + private type() { + return this.config.type ?? "hybrid"; + } + public async length(): Promise { const txs = await this.transactionStorage.getPendingUserTransactions(0); return txs.length; @@ -81,6 +92,10 @@ export class PrivateMempool extends SequencerModule implements Mempool { offset?: number, limit?: number ): Promise { + if (this.type() === "based") { + return []; + } + let txs = await this.transactionStorage.getPendingUserTransactions( offset ?? 0, limit @@ -95,6 +110,9 @@ export class PrivateMempool extends SequencerModule implements Mempool { @trace("mempool.get_mandatory_txs") public async getMandatoryTxs(): Promise { + if (this.type() === "private") { + return []; + } return (await this.messageService?.getPendingMessages()) ?? []; } From 0ee566966a02313079bdb849d8c0b83f95975839 Mon Sep 17 00:00:00 2001 From: Raphael Panic Date: Sat, 17 Jan 2026 16:20:05 +0100 Subject: [PATCH 9/9] Fixed tests --- packages/sdk/test/fees-multi-zkprograms.test.ts | 4 +--- packages/sequencer/test-integration/benchmarks/tps.test.ts | 4 +--- packages/sequencer/test/integration/Block-order.test.ts | 4 +--- .../sequencer/test/integration/BlockProductionSize.test.ts | 4 +--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/sdk/test/fees-multi-zkprograms.test.ts b/packages/sdk/test/fees-multi-zkprograms.test.ts index 53d4389f..e6921b70 100644 --- a/packages/sdk/test/fees-multi-zkprograms.test.ts +++ b/packages/sdk/test/fees-multi-zkprograms.test.ts @@ -185,9 +185,7 @@ describe("check fee analyzer", () => { }, }, Sequencer: { - Mempool: { - validationEnabled: true, - }, + Mempool: {}, }, }); diff --git a/packages/sequencer/test-integration/benchmarks/tps.test.ts b/packages/sequencer/test-integration/benchmarks/tps.test.ts index b3eca009..68e2dd5a 100644 --- a/packages/sequencer/test-integration/benchmarks/tps.test.ts +++ b/packages/sequencer/test-integration/benchmarks/tps.test.ts @@ -104,9 +104,7 @@ export async function createAppChain() { maximumBlockSize: 100, }, BlockTrigger: {}, - Mempool: { - validationEnabled: false, - }, + Mempool: {}, }, Signer: { signer: PrivateKey.random(), diff --git a/packages/sequencer/test/integration/Block-order.test.ts b/packages/sequencer/test/integration/Block-order.test.ts index 0f3ee564..e13aaf56 100644 --- a/packages/sequencer/test/integration/Block-order.test.ts +++ b/packages/sequencer/test/integration/Block-order.test.ts @@ -98,9 +98,7 @@ describe.each([["InMemory", InMemoryDatabase]])( Sequencer: { Database: {}, BlockTrigger: {}, - Mempool: { - validationEnabled: true, - }, + Mempool: {}, FeeStrategy: {}, BatchProducerModule: {}, BlockProducerModule: {}, diff --git a/packages/sequencer/test/integration/BlockProductionSize.test.ts b/packages/sequencer/test/integration/BlockProductionSize.test.ts index 109b576c..67d6d7ca 100644 --- a/packages/sequencer/test/integration/BlockProductionSize.test.ts +++ b/packages/sequencer/test/integration/BlockProductionSize.test.ts @@ -70,9 +70,7 @@ describe("block limit", () => { Sequencer: { Database: {}, BlockTrigger: {}, - Mempool: { - validationEnabled: true, - }, + Mempool: {}, BatchProducerModule: {}, BlockProducerModule: { maximumBlockSize: maxBlockSize,