Skip to content

Commit a64749c

Browse files
authored
Fix RPC chain tracker (#198)
This PR makes sure the starting cursor is in the chain tracker if it's a non-reorged block.
2 parents d166d89 + a0ab942 commit a64749c

3 files changed

Lines changed: 38 additions & 12 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "protocol: insert starting cursor in chain tracker",
4+
"packageName": "@apibara/protocol",
5+
"email": "francesco@ceccon.me",
6+
"dependentChangeType": "patch"
7+
}

packages/protocol/src/rpc/chain-tracker.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ export class ChainTracker {
4343
}
4444

4545
updateFinalized(newFinalized: BlockInfo) {
46+
// console.debug(
47+
// `updateFinalized: new=${newFinalized.blockNumber} old=${this.#finalized.blockNumber}`,
48+
// );
49+
4650
if (newFinalized.blockNumber < this.#finalized.blockNumber) {
4751
throw new Error("Finalized cursor moved backwards");
4852
}
@@ -70,7 +74,9 @@ export class ChainTracker {
7074
return true;
7175
}
7276

73-
addToCanonicalChain(blockInfo: BlockInfo) {
77+
addToCanonicalChain({ blockInfo }: { blockInfo: BlockInfo }) {
78+
// console.debug(`addToCanonicalChain: block=${blockInfo.blockNumber}`);
79+
7480
const existing = this.#canonical.get(blockInfo.blockNumber);
7581

7682
if (existing) {
@@ -83,7 +89,7 @@ export class ChainTracker {
8389

8490
const parent = this.#canonical.get(blockInfo.blockNumber - 1n);
8591
if (!parent) {
86-
throw new Error("Parent block not found");
92+
throw new Error("Parent block not in canonical chain");
8793
}
8894

8995
if (parent.blockHash !== blockInfo.parentBlockHash) {
@@ -101,6 +107,10 @@ export class ChainTracker {
101107
newHead,
102108
fetchCursorByHash,
103109
}: UpdateHeadArgs): Promise<UpdateHeadResult> {
110+
// console.debug(
111+
// `updateHead: new=${newHead.blockNumber} old=${this.#head.blockNumber}`,
112+
// );
113+
104114
// No changes to the chain.
105115
if (
106116
newHead.blockNumber === this.#head.blockNumber &&
@@ -245,21 +255,24 @@ export class ChainTracker {
245255
| { canonical: false; reason: string; fullCursor?: undefined }
246256
> {
247257
const head = this.head();
258+
const finalized = this.finalized();
259+
248260
if (cursor.orderKey > head.orderKey) {
249261
return { canonical: false, reason: "cursor is ahead of head" };
250262
}
251263

264+
const expectedInfo = await fetchCursor(cursor.orderKey);
252265
if (!cursor.uniqueKey) {
253-
const fullInfo = await fetchCursor(cursor.orderKey);
254-
255-
if (fullInfo === null) {
266+
if (expectedInfo === null) {
256267
throw new Error("Failed to initialize canonical cursor");
257268
}
258269

259-
return { canonical: true, fullCursor: blockInfoToCursor(fullInfo) };
260-
}
270+
if (expectedInfo.blockNumber > finalized.orderKey) {
271+
this.#canonical.set(expectedInfo.blockNumber, expectedInfo);
272+
}
261273

262-
const expectedInfo = await fetchCursor(cursor.orderKey);
274+
return { canonical: true, fullCursor: blockInfoToCursor(expectedInfo) };
275+
}
263276

264277
if (expectedInfo === null) {
265278
return {
@@ -295,6 +308,10 @@ export class ChainTracker {
295308
};
296309
}
297310

311+
if (expectedInfo.blockNumber > finalized.orderKey) {
312+
this.#canonical.set(expectedInfo.blockNumber, expectedInfo);
313+
}
314+
298315
return { canonical: true, fullCursor: expectedCursor };
299316
}
300317
}

packages/protocol/src/rpc/data-stream.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ async function* dataStreamLoop<TFilter, TBlock>(
142142

143143
const finalized = chainTracker.finalized();
144144

145-
// console.log(
146-
// `Loop: c=${cursor.orderKey} f=${finalized.orderKey} h=${chainTracker.head().orderKey}`,
145+
// console.debug(
146+
// `RpcLoop: c=${cursor.orderKey} f=${finalized.orderKey} h=${chainTracker.head().orderKey}`,
147147
// );
148148

149149
if (cursor.orderKey < finalized.orderKey) {
@@ -200,6 +200,7 @@ async function* backfillFinalizedBlocks<TFilter, TBlock>(
200200
};
201201
}
202202

203+
// Notice that we check that filteredData.endBlock <= finalized.orderKey above.
203204
if (filterData.endBlock === finalized.orderKey) {
204205
// Prepare for transition to non-finalized data.
205206
state.cursor = finalized;
@@ -240,8 +241,9 @@ async function* produceNextBlock<TFilter, TBlock>(
240241
uniqueKey: blockInfo.blockHash,
241242
};
242243

243-
const { status: headUpdateStatus } =
244-
state.chainTracker.addToCanonicalChain(blockInfo);
244+
const { status: headUpdateStatus } = state.chainTracker.addToCanonicalChain({
245+
blockInfo,
246+
});
245247

246248
if (headUpdateStatus !== "success") {
247249
throw new Error("Failed to update head. Would cause reorg.");

0 commit comments

Comments
 (0)