Skip to content

Commit 9850552

Browse files
committed
getLatestProposedBundleData should never load data from scratch
1 parent 55783b7 commit 9850552

File tree

2 files changed

+76
-31
lines changed

2 files changed

+76
-31
lines changed

src/clients/BundleDataClient/BundleDataClient.ts

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -240,13 +240,7 @@ export class BundleDataClient {
240240
);
241241
}
242242

243-
private async loadPersistedDataFromArweave(
244-
blockRangesForChains: number[][]
245-
): Promise<LoadDataReturnValue | undefined> {
246-
if (!isDefined(this.clients?.arweaveClient)) {
247-
return undefined;
248-
}
249-
const start = performance.now();
243+
private async getBundleDataFromArweave(blockRangesForChains: number[][]) {
250244
const persistedData = await this.clients.arweaveClient.getByTopic(
251245
this.getArweaveBundleDataClientKey(blockRangesForChains),
252246
BundleDataSS
@@ -256,6 +250,20 @@ export class BundleDataClient {
256250
if (!isDefined(persistedData) || persistedData.length < 1) {
257251
return undefined;
258252
}
253+
return persistedData;
254+
}
255+
256+
private async loadPersistedDataFromArweave(
257+
blockRangesForChains: number[][]
258+
): Promise<LoadDataReturnValue | undefined> {
259+
if (!isDefined(this.clients?.arweaveClient)) {
260+
return undefined;
261+
}
262+
const start = performance.now();
263+
const persistedData = await this.getBundleDataFromArweave(blockRangesForChains);
264+
if (!isDefined(persistedData)) {
265+
return undefined;
266+
}
259267

260268
// A converter function to account for the fact that our SuperStruct schema does not support numeric
261269
// keys in records. Fundamentally, this is a limitation of superstruct itself.
@@ -435,24 +443,39 @@ export class BundleDataClient {
435443
const hubPoolClient = this.clients.hubPoolClient;
436444
// Determine which bundle we should fetch from arweave, either the pending bundle or the latest
437445
// executed one. Both should have arweave data but if for some reason the arweave data is missing,
438-
// this function will have to compute the bundle data from scratch which will be slow. We have to fallback
439-
// to computing the bundle from scratch since this function needs to return the full bundle data so that
440-
// it can be used to get the running balance proposed using its data.
441-
const bundleBlockRanges = getImpliedBundleBlockRanges(
446+
// this function will load the bundle data from the most recent bundle data published to Arweave.
447+
448+
let bundleBlockRanges = getImpliedBundleBlockRanges(
442449
hubPoolClient,
443450
this.clients.configStoreClient,
444451
hubPoolClient.hasPendingProposal()
445452
? hubPoolClient.getLatestProposedRootBundle()
446-
: hubPoolClient.getLatestFullyExecutedRootBundle(hubPoolClient.latestBlockSearched)! // ! because we know there is a bundle
453+
: hubPoolClient.getNthFullyExecutedRootBundle(-1)!
447454
);
448-
return {
449-
blockRanges: bundleBlockRanges,
450-
bundleData: await this.loadData(
451-
bundleBlockRanges,
452-
this.spokePoolClients,
453-
true // this bundle data should have been published to arweave
454-
),
455-
};
455+
// Check if bundle data exists on arweave, otherwise fallback to last published bundle data. If the
456+
// first bundle block range we are trying is the pending proposal, then we'll grab the most recently
457+
// validated bundle, otherwise we'll grab the second most recently validated bundle.
458+
let n = hubPoolClient.hasPendingProposal() ? 1 : 2;
459+
460+
// eslint-disable-next-line no-constant-condition
461+
while (true) {
462+
const bundleDataOnArweave = await this.getBundleDataFromArweave(bundleBlockRanges);
463+
if (!isDefined(bundleDataOnArweave)) {
464+
// Bundle data is not arweave, try the next most recently validated bundle.
465+
bundleBlockRanges = getImpliedBundleBlockRanges(
466+
hubPoolClient,
467+
this.clients.configStoreClient,
468+
hubPoolClient.getNthFullyExecutedRootBundle(-n)!
469+
);
470+
} else {
471+
return {
472+
blockRanges: bundleBlockRanges,
473+
bundleData: await this.loadData(bundleBlockRanges, this.spokePoolClients, true),
474+
};
475+
}
476+
477+
n++;
478+
}
456479
}
457480

458481
async getLatestPoolRebalanceRoot(): Promise<{ root: PoolRebalanceRoot; blockRanges: number[][] }> {
@@ -992,11 +1015,7 @@ export class BundleDataClient {
9921015
}
9931016
}
9941017
} else {
995-
this.logger.warn({
996-
at: "BundleDataClient#loadDataFromScratch",
997-
message: "Detected duplicate fill",
998-
fill,
999-
});
1018+
throw new Error("Duplicate fill detected");
10001019
}
10011020
return;
10021021
}
@@ -1119,11 +1138,7 @@ export class BundleDataClient {
11191138
validatedBundleSlowFills.push(matchedDeposit);
11201139
}
11211140
} else {
1122-
this.logger.warn({
1123-
at: "BundleDataClient#loadDataFromScratch",
1124-
message: "Detected duplicate slow fill request",
1125-
slowFillRequest,
1126-
});
1141+
throw new Error("Duplicate slow fill request detected.");
11271142
}
11281143
return;
11291144
}

src/clients/SpokePoolClient.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,13 +726,27 @@ export class SpokePoolClient extends BaseAbstractClient {
726726
const slowFillRequest = {
727727
...spreadEventWithBlockNumber(event),
728728
destinationChainId: this.chainId,
729-
} as SlowFillRequestWithBlock;
729+
} as SlowFillRequestWithBlock;
730730

731731
if (eventName === "RequestedV3SlowFill") {
732732
slowFillRequest.messageHash = getMessageHash(slowFillRequest.message);
733733
}
734734

735735
const depositHash = getRelayEventKey({ ...slowFillRequest, destinationChainId: this.chainId });
736+
737+
// Sanity check that this event is not a duplicate.
738+
if (
739+
this.slowFillRequests[depositHash] !== undefined
740+
) {
741+
this.logger.warn({
742+
at: "SpokePoolClient#update",
743+
chainId: this.chainId,
744+
message: "Duplicate slow fill request found",
745+
slowFillRequest,
746+
});
747+
continue;
748+
}
749+
736750
this.slowFillRequests[depositHash] ??= slowFillRequest;
737751
}
738752
};
@@ -766,6 +780,22 @@ export class SpokePoolClient extends BaseAbstractClient {
766780
fill.relayExecutionInfo.updatedMessageHash = getMessageHash(event.args.relayExecutionInfo.updatedMessage);
767781
}
768782

783+
// Sanity check that this event is not a duplicate.
784+
if (
785+
this.fills[fill.originChainId] !== undefined &&
786+
this.fills[fill.originChainId].some(
787+
(f) => f.transactionHash === fill.transactionHash && f.logIndex === fill.logIndex
788+
)
789+
) {
790+
this.logger.warn({
791+
at: "SpokePoolClient#update",
792+
chainId: this.chainId,
793+
message: "Duplicate fill found",
794+
fill,
795+
});
796+
continue;
797+
}
798+
769799
assign(this.fills, [fill.originChainId], [fill]);
770800
assign(this.depositHashesToFills, [this.getDepositHash(fill)], [fill]);
771801
}

0 commit comments

Comments
 (0)