Skip to content

Commit 242490e

Browse files
authored
Merge pull request #209 from proto-kit/feature/configurable_block_size
Make Block Size Configurable
2 parents 9ab55c9 + 0366e68 commit 242490e

File tree

4 files changed

+157
-6
lines changed

4 files changed

+157
-6
lines changed

packages/sequencer/src/mempool/Mempool.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ export interface Mempool<Events extends MempoolEvents = MempoolEvents>
1717
/**
1818
* Retrieve all transactions that are currently in the mempool
1919
*/
20-
getTxs: () => Promise<PendingTransaction[]>;
20+
getTxs: (limit?: number) => Promise<PendingTransaction[]>;
2121
}

packages/sequencer/src/mempool/private/PrivateMempool.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export class PrivateMempool extends SequencerModule implements Mempool {
9393
return result?.result.afterNetworkState;
9494
}
9595

96-
public async getTxs(): Promise<PendingTransaction[]> {
96+
public async getTxs(limit?: number): Promise<PendingTransaction[]> {
9797
const txs = await this.transactionStorage.getPendingUserTransactions();
9898
const executionContext = container.resolve<RuntimeMethodExecutionContext>(
9999
RuntimeMethodExecutionContext
@@ -108,7 +108,8 @@ export class PrivateMempool extends SequencerModule implements Mempool {
108108
baseCachedStateService,
109109
this.protocol.stateServiceProvider,
110110
networkState,
111-
executionContext
111+
executionContext,
112+
limit
112113
);
113114
this.protocol.stateServiceProvider.popCurrentStateService();
114115
return sortedTxs;
@@ -128,13 +129,17 @@ export class PrivateMempool extends SequencerModule implements Mempool {
128129
baseService: CachedStateService,
129130
stateServiceProvider: StateServiceProvider,
130131
networkState: NetworkState,
131-
executionContext: RuntimeMethodExecutionContext
132+
executionContext: RuntimeMethodExecutionContext,
133+
limit?: number
132134
): Promise<PendingTransaction[]> {
133135
const skippedTransactions: Record<string, MempoolTransactionPaths> = {};
134136
const sortedTransactions: PendingTransaction[] = [];
135137
let queue: PendingTransaction[] = [...transactions];
136138

137-
while (queue.length > 0) {
139+
while (
140+
queue.length > 0 &&
141+
(limit !== undefined ? sortedTransactions.length < limit : true)
142+
) {
138143
const [tx] = queue.splice(0, 1);
139144
const txStateService = new CachedStateService(baseService);
140145
stateServiceProvider.setCurrentStateService(txStateService);

packages/sequencer/src/protocol/production/sequencing/BlockProducerModule.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { TransactionExecutionService } from "./TransactionExecutionService";
2626

2727
export interface BlockConfig {
2828
allowEmptyBlock?: boolean;
29+
maximumBlockSize?: number;
2930
}
3031

3132
@sequencerModule()
@@ -55,6 +56,10 @@ export class BlockProducerModule extends SequencerModule<BlockConfig> {
5556
return this.config.allowEmptyBlock ?? true;
5657
}
5758

59+
private maximumBlockSize() {
60+
return this.config.maximumBlockSize ?? 20;
61+
}
62+
5863
private prettyPrintBlockContents(block: Block) {
5964
block.transactions.forEach((tx, i) => {
6065
const methodName = this.methodIdResolver.getMethodNameFromId(
@@ -144,7 +149,7 @@ export class BlockProducerModule extends SequencerModule<BlockConfig> {
144149
txs: PendingTransaction[];
145150
metadata: BlockWithResult;
146151
}> {
147-
const txs = await this.mempool.getTxs();
152+
const txs = await this.mempool.getTxs(this.maximumBlockSize());
148153

149154
const parentBlock = await this.blockQueue.getLatestBlock();
150155

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { log } from "@proto-kit/common";
2+
import { VanillaProtocolModules } from "@proto-kit/library";
3+
import { Runtime } from "@proto-kit/module";
4+
import { Protocol } from "@proto-kit/protocol";
5+
import { AppChain } from "@proto-kit/sdk";
6+
import { Bool, PrivateKey, Struct, UInt64 } from "o1js";
7+
import "reflect-metadata";
8+
import { container } from "tsyringe";
9+
10+
import { ManualBlockTrigger, PrivateMempool, Sequencer } from "../../src";
11+
import {
12+
DefaultTestingSequencerModules,
13+
testingSequencerFromModules,
14+
} from "../TestingSequencer";
15+
16+
import { Balance } from "./mocks/Balance";
17+
import { ProtocolStateTestHook } from "./mocks/ProtocolStateTestHook";
18+
import { createTransaction } from "./utils";
19+
import { NoopRuntime } from "./mocks/NoopRuntime";
20+
21+
export class PrimaryTestEvent extends Struct({
22+
message: Bool,
23+
}) {}
24+
25+
export class SecondaryTestEvent extends Struct({
26+
message: Bool,
27+
}) {}
28+
29+
describe("block limit", () => {
30+
let runtime: Runtime<{
31+
Balance: typeof Balance;
32+
NoopRuntime: typeof NoopRuntime;
33+
}>;
34+
let sequencer: Sequencer<DefaultTestingSequencerModules>;
35+
36+
let blockTrigger: ManualBlockTrigger;
37+
let mempool: PrivateMempool;
38+
39+
log.setLevel(log.levels.INFO);
40+
41+
const runtimeClass = Runtime.from({
42+
modules: {
43+
Balance,
44+
NoopRuntime,
45+
},
46+
47+
config: {
48+
Balance: {},
49+
NoopRuntime: {},
50+
},
51+
});
52+
53+
async function setUpAppChain(maxBlockSize: number | undefined) {
54+
const sequencerClass = testingSequencerFromModules({});
55+
56+
const protocolClass = Protocol.from({
57+
modules: VanillaProtocolModules.mandatoryModules({
58+
ProtocolStateTestHook,
59+
}),
60+
});
61+
62+
const app = AppChain.from({
63+
Runtime: runtimeClass,
64+
Sequencer: sequencerClass,
65+
Protocol: protocolClass,
66+
modules: {},
67+
});
68+
log.setLevel("TRACE");
69+
70+
app.configure({
71+
Sequencer: {
72+
Database: {},
73+
BlockTrigger: {},
74+
Mempool: {},
75+
BatchProducerModule: {},
76+
BlockProducerModule: {
77+
maximumBlockSize: maxBlockSize,
78+
},
79+
LocalTaskWorkerModule: {},
80+
BaseLayer: {},
81+
TaskQueue: {},
82+
FeeStrategy: {},
83+
ProtocolStartupModule: {},
84+
},
85+
Runtime: {
86+
Balance: {},
87+
NoopRuntime: {},
88+
},
89+
Protocol: {
90+
AccountState: {},
91+
BlockProver: {},
92+
StateTransitionProver: {},
93+
BlockHeight: {},
94+
LastStateRoot: {},
95+
ProtocolStateTestHook: {},
96+
},
97+
});
98+
99+
// Start AppChain
100+
await app.start(container.createChildContainer());
101+
102+
({ runtime, sequencer } = app);
103+
104+
mempool = sequencer.resolve("Mempool");
105+
106+
const privateKey = PrivateKey.random();
107+
108+
for (let i = 0; i < 40; i++) {
109+
const tx = createTransaction({
110+
runtime,
111+
method: ["Balance", "setBalanceIf"],
112+
privateKey: privateKey,
113+
args: [privateKey.toPublicKey(), UInt64.from(100), Bool(true)],
114+
nonce: i,
115+
});
116+
await mempool.add(tx);
117+
}
118+
}
119+
120+
it.each([
121+
[5, 5],
122+
[10, 10],
123+
[15, 15],
124+
[25, 25],
125+
[35, 35],
126+
[undefined, 20],
127+
])(
128+
"when limit is set to %p should produce block with maximum size %p",
129+
async (limit, maxValue) => {
130+
await setUpAppChain(limit);
131+
132+
blockTrigger = sequencer.resolve("BlockTrigger");
133+
134+
const block = await blockTrigger.produceBlock();
135+
136+
expect(block).toBeDefined();
137+
expect(block!.transactions).toHaveLength(maxValue);
138+
},
139+
30000
140+
);
141+
});

0 commit comments

Comments
 (0)