From 79b4b5b4344dc7814cbea41dec61dd261a18ccec Mon Sep 17 00:00:00 2001 From: Suning Yao Date: Fri, 13 Oct 2023 18:55:10 +0800 Subject: [PATCH] Add configurable sequencer orderings --- packages/node/src/Application.ts | 1 + packages/node/src/core/BatchPoster.test.ts | 68 ++++++++++++++++++++++ packages/node/src/core/BatchPoster.ts | 31 +++++++++- 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/packages/node/src/Application.ts b/packages/node/src/Application.ts index a9c0750..03b9b54 100644 --- a/packages/node/src/Application.ts +++ b/packages/node/src/Application.ts @@ -90,6 +90,7 @@ export class Application { logger, calculateTransactionLimit(config.batchPoster.gasLimit), config.batchPoster.intervalMs, + config.batchPoster.sequencerOrder, ) const routers: AppRouters = { diff --git a/packages/node/src/core/BatchPoster.test.ts b/packages/node/src/core/BatchPoster.test.ts index 0293dc2..2cb2ac6 100644 --- a/packages/node/src/core/BatchPoster.test.ts +++ b/packages/node/src/core/BatchPoster.test.ts @@ -61,6 +61,7 @@ describe(BatchPoster.name, () => { const FLUSH_PERIOD_MS = 1_000 const TRANSACTION_LIMIT = 100 + const SEQUENCER_ORDER = 'FEE' describe(BatchPoster.prototype.start.name, () => { it('submits transactions where every one applies every flush period seconds', async () => { @@ -103,6 +104,7 @@ describe(BatchPoster.name, () => { Logger.SILENT, TRANSACTION_LIMIT, FLUSH_PERIOD_MS, + SEQUENCER_ORDER, ) batchPoster.start() await time.tickAsync(FLUSH_PERIOD_MS * 3) @@ -160,6 +162,7 @@ describe(BatchPoster.name, () => { Logger.SILENT, TRANSACTION_LIMIT, FLUSH_PERIOD_MS, + SEQUENCER_ORDER, ) batchPoster.start() await time.tickAsync(FLUSH_PERIOD_MS * 3) @@ -213,6 +216,7 @@ describe(BatchPoster.name, () => { Logger.SILENT, TRANSACTION_LIMIT, FLUSH_PERIOD_MS, + SEQUENCER_ORDER, ) batchPoster.start() await time.tickAsync(FLUSH_PERIOD_MS * 3) @@ -263,6 +267,7 @@ describe(BatchPoster.name, () => { Logger.SILENT, TRANSACTION_LIMIT, FLUSH_PERIOD_MS, + SEQUENCER_ORDER, ) batchPoster.start() await time.tickAsync(FLUSH_PERIOD_MS * 3) @@ -320,6 +325,7 @@ describe(BatchPoster.name, () => { Logger.SILENT, TRANSACTION_LIMIT, FLUSH_PERIOD_MS, + SEQUENCER_ORDER, ) batchPoster.start() await time.tickAsync(FLUSH_PERIOD_MS * 3) @@ -369,6 +375,7 @@ describe(BatchPoster.name, () => { Logger.SILENT, TRANSACTION_LIMIT, FLUSH_PERIOD_MS, + SEQUENCER_ORDER, ) batchPoster.start() await time.tickAsync(FLUSH_PERIOD_MS * 3) @@ -420,6 +427,7 @@ describe(BatchPoster.name, () => { Logger.SILENT, TRANSACTION_LIMIT, FLUSH_PERIOD_MS, + SEQUENCER_ORDER, ) batchPoster.start() await time.tickAsync(FLUSH_PERIOD_MS * 3) @@ -436,5 +444,65 @@ describe(BatchPoster.name, () => { modelTx2SerializedHex, ) }) + + it('submits transactions every flush period seconds with different sequencer ordering', async () => { + const stateUpdater = mockObject({ + getState: mockFn() + .returnsOnce({ + '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266': { + balance: 100n, + nonce: 0n, + }, + '0x70997970C51812dc3A010C7d01b50e0d17dc79C8': { + balance: 500n, + nonce: 0n, + }, + }) + .returnsOnce({ + '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266': { + balance: 88n, + nonce: 1n, + }, + '0x70997970C51812dc3A010C7d01b50e0d17dc79C8': { + balance: 0n, + nonce: 1n, + }, + }), + }) + const client = mockObject({ + writeToInputsContract: mockFn() + .throwsOnce(new Error('failed')) + .returns(null), + }) + const mempool = mockObject({ + popNFIFO: mockFn() + .returnsOnce([modelSignedTx1]) + .returnsOnce([modelSignedTx2]), + empty: mockFn().returns(null), + }) + const batchPoster = new BatchPoster( + stateUpdater, + client, + mempool, + Logger.SILENT, + TRANSACTION_LIMIT, + FLUSH_PERIOD_MS, + "FIFO", + ) + batchPoster.start() + await time.tickAsync(FLUSH_PERIOD_MS * 3) + + expect(mempool.empty).toHaveBeenCalledTimes(0) + expect(mempool.popNFIFO).toHaveBeenCalledTimes(2) + expect(client.writeToInputsContract).toHaveBeenCalledTimes(2) + expect(client.writeToInputsContract).toHaveBeenNthCalledWith( + 1, + modelTx1SerializedHex, + ) + expect(client.writeToInputsContract).toHaveBeenNthCalledWith( + 2, + modelTx2SerializedHex, + ) + }) }) }) diff --git a/packages/node/src/core/BatchPoster.ts b/packages/node/src/core/BatchPoster.ts index 2d0b984..d0b462b 100644 --- a/packages/node/src/core/BatchPoster.ts +++ b/packages/node/src/core/BatchPoster.ts @@ -15,6 +15,7 @@ export class BatchPoster { private readonly logger: Logger, private readonly transactionLimit: number, private readonly intervalMs: number, + private readonly sequencerOrder: string, ) { this.logger = logger.for(this) } @@ -29,9 +30,33 @@ export class BatchPoster { } private async postBatch(): Promise { - const candidateTransactions = this.mempool.popNHighestFee( - this.transactionLimit, - ) + let candidateTransactions + switch (this.sequencerOrder) { + case 'FEE': { + candidateTransactions = this.mempool.popNHighestFee(this.transactionLimit) + break + } + case 'FIFO': { + candidateTransactions = this.mempool.popNFIFO(this.transactionLimit) + break + } + case 'LIFO': { + candidateTransactions = this.mempool.popNLIFO(this.transactionLimit) + break + } + case 'RANDOM': { + candidateTransactions = this.mempool.popNRandom(this.transactionLimit) + break + } + case 'VALUE': { + candidateTransactions = this.mempool.popNHighestValue(this.transactionLimit) + break + } + default: { + candidateTransactions = this.mempool.popNHighestFee(this.transactionLimit) + } + } + if (candidateTransactions.length === 0) { return }