Skip to content

Commit

Permalink
Merge branch 'master' into remove-fallback-gas-price-oracle
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholaspai authored Feb 13, 2025
2 parents 5ebd7b4 + 3066c7b commit 5bf7ac5
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 60 deletions.
69 changes: 20 additions & 49 deletions src/clients/BundleDataClient/BundleDataClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
Deposit,
DepositWithBlock,
} from "../../interfaces";
import { AcrossConfigStoreClient, SpokePoolClient } from "..";
import { SpokePoolClient } from "..";
import {
BigNumber,
bnZero,
Expand All @@ -41,18 +41,17 @@ import {
isZeroValueFillOrSlowFillRequest,
chainIsEvm,
isValidEvmAddress,
duplicateEvent,
} from "../../utils";
import winston from "winston";
import {
_buildPoolRebalanceRoot,
BundleData,
BundleDataSS,
getEndBlockBuffers,
getRefundInformationFromFill,
getRefundsFromBundle,
getWidestPossibleExpectedBlockRange,
isChainDisabled,
PoolRebalanceRoot,
prettyPrintV3SpokePoolEvents,
V3DepositWithBlock,
V3FillWithBlock,
Expand Down Expand Up @@ -431,52 +430,6 @@ export class BundleDataClient {
}, toBN(0));
}

private async getLatestProposedBundleData(): Promise<{ bundleData: LoadDataReturnValue; blockRanges: number[][] }> {
const hubPoolClient = this.clients.hubPoolClient;
// Determine which bundle we should fetch from arweave, either the pending bundle or the latest
// executed one. Both should have arweave data but if for some reason the arweave data is missing,
// this function will have to compute the bundle data from scratch which will be slow. We have to fallback
// to computing the bundle from scratch since this function needs to return the full bundle data so that
// it can be used to get the running balance proposed using its data.
const bundleBlockRanges = getImpliedBundleBlockRanges(
hubPoolClient,
this.clients.configStoreClient,
hubPoolClient.hasPendingProposal()
? hubPoolClient.getLatestProposedRootBundle()
: hubPoolClient.getLatestFullyExecutedRootBundle(hubPoolClient.latestBlockSearched)! // ! because we know there is a bundle
);
return {
blockRanges: bundleBlockRanges,
bundleData: await this.loadData(
bundleBlockRanges,
this.spokePoolClients,
true // this bundle data should have been published to arweave
),
};
}

async getLatestPoolRebalanceRoot(): Promise<{ root: PoolRebalanceRoot; blockRanges: number[][] }> {
const { bundleData, blockRanges } = await this.getLatestProposedBundleData();
const hubPoolClient = this.clients.hubPoolClient;
const root = _buildPoolRebalanceRoot(
hubPoolClient.latestBlockSearched,
blockRanges[0][1],
bundleData.bundleDepositsV3,
bundleData.bundleFillsV3,
bundleData.bundleSlowFillsV3,
bundleData.unexecutableSlowFills,
bundleData.expiredDepositsToRefundV3,
{
hubPoolClient,
configStoreClient: hubPoolClient.configStoreClient as AcrossConfigStoreClient,
}
);
return {
root,
blockRanges,
};
}

// @dev This function should probably be moved to the InventoryClient since it bypasses loadData completely now.
// Return refunds from the next valid bundle. This will contain any refunds that have been sent but are not included
// in a valid bundle with all of its leaves executed. This contains refunds from:
Expand Down Expand Up @@ -867,6 +820,14 @@ export class BundleDataClient {
"Not using correct bundle deposit hash key"
);
if (deposit.blockNumber >= originChainBlockRange[0]) {
if (bundleDepositsV3?.[originChainId]?.[deposit.inputToken]?.find((d) => duplicateEvent(deposit, d))) {
this.logger.debug({
at: "BundleDataClient#loadData",
message: "Duplicate deposit detected",
deposit,
});
throw new Error("Duplicate deposit detected");
}
bundleDepositHashes.push(newBundleDepositHash);
updateBundleDepositsV3(bundleDepositsV3, deposit);
} else if (deposit.blockNumber < originChainBlockRange[0]) {
Expand Down Expand Up @@ -978,6 +939,11 @@ export class BundleDataClient {
}
}
} else {
this.logger.debug({
at: "BundleDataClient#loadData",
message: "Duplicate fill detected",
fill,
});
throw new Error("Duplicate fill detected");
}
return;
Expand Down Expand Up @@ -1099,6 +1065,11 @@ export class BundleDataClient {
validatedBundleSlowFills.push(matchedDeposit);
}
} else {
this.logger.debug({
at: "BundleDataClient#loadData",
message: "Duplicate slow fill request detected",
slowFillRequest,
});
throw new Error("Duplicate slow fill request detected.");
}
return;
Expand Down
31 changes: 20 additions & 11 deletions src/clients/HubPoolClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -776,22 +776,31 @@ export class HubPoolClient extends BaseAbstractClient {
return endBlock > 0 ? endBlock + 1 : 0;
}

getRunningBalanceBeforeBlockForChain(block: number, chain: number, l1Token: string): TokenRunningBalance {
getLatestExecutedRootBundleContainingL1Token(
block: number,
chain: number,
l1Token: string
): ExecutedRootBundle | undefined {
// Search ExecutedRootBundles in descending block order to find the most recent event before the target block.
const executedRootBundle = sortEventsDescending(this.executedRootBundles).find(
(executedLeaf: ExecutedRootBundle) => {
return (
executedLeaf.blockNumber <= block &&
executedLeaf.chainId === chain &&
executedLeaf.l1Tokens.map((l1Token) => l1Token.toLowerCase()).includes(l1Token.toLowerCase())
);
}
) as ExecutedRootBundle;
return sortEventsDescending(this.executedRootBundles).find((executedLeaf: ExecutedRootBundle) => {
return (
executedLeaf.blockNumber <= block &&
executedLeaf.chainId === chain &&
executedLeaf.l1Tokens.some((token) => token.toLowerCase() === l1Token.toLowerCase())
);
});
}

getRunningBalanceBeforeBlockForChain(block: number, chain: number, l1Token: string): TokenRunningBalance {
const executedRootBundle = this.getLatestExecutedRootBundleContainingL1Token(block, chain, l1Token);

return this.getRunningBalanceForToken(l1Token, executedRootBundle);
}

public getRunningBalanceForToken(l1Token: string, executedRootBundle: ExecutedRootBundle): TokenRunningBalance {
public getRunningBalanceForToken(
l1Token: string,
executedRootBundle: ExecutedRootBundle | undefined
): TokenRunningBalance {
let runningBalance = toBN(0);
if (executedRootBundle) {
const indexOfL1Token = executedRootBundle.l1Tokens
Expand Down
29 changes: 29 additions & 0 deletions src/clients/SpokePoolClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
toAddress,
} from "../utils";
import {
duplicateEvent,
paginatedEventQuery,
sortEventsAscendingInPlace,
spreadEvent,
Expand Down Expand Up @@ -585,6 +586,7 @@ export class SpokePoolClient extends BaseAbstractClient {
* @see _update
*/
public async update(eventsToQuery = this.queryableEventNames): Promise<void> {
const duplicateEvents: Log[] = [];
if (this.hubPoolClient !== null && !this.hubPoolClient.isUpdated) {
throw new Error("HubPoolClient not updated");
}
Expand Down Expand Up @@ -654,6 +656,12 @@ export class SpokePoolClient extends BaseAbstractClient {
}

if (this.depositHashes[getRelayEventKey(deposit)] !== undefined) {
// Sanity check that this event is not a duplicate, even though the relay data hash is a duplicate.
const allDeposits = this._getDuplicateDeposits(deposit).concat(this.depositHashes[getRelayEventKey(deposit)]);
if (allDeposits.some((e) => duplicateEvent(deposit, e))) {
duplicateEvents.push(event);
continue;
}
assign(this.duplicateDepositHashes, [getRelayEventKey(deposit)], [deposit]);
continue;
}
Expand Down Expand Up @@ -716,6 +724,13 @@ export class SpokePoolClient extends BaseAbstractClient {
}

const depositHash = getRelayEventKey({ ...slowFillRequest, destinationChainId: this.chainId });

// Sanity check that this event is not a duplicate.
if (this.slowFillRequests[depositHash] !== undefined) {
duplicateEvents.push(event);
continue;
}

this.slowFillRequests[depositHash] ??= slowFillRequest;
}
};
Expand Down Expand Up @@ -749,6 +764,13 @@ export class SpokePoolClient extends BaseAbstractClient {
fill.relayExecutionInfo.updatedMessageHash = getMessageHash(event.args.relayExecutionInfo.updatedMessage);
}

// Sanity check that this event is not a duplicate.
const duplicateFill = this.fills[fill.originChainId]?.find((f) => duplicateEvent(fill, f));
if (duplicateFill) {
duplicateEvents.push(event);
continue;
}

assign(this.fills, [fill.originChainId], [fill]);
assign(this.depositHashesToFills, [this.getDepositHash(fill)], [fill]);
}
Expand Down Expand Up @@ -793,6 +815,13 @@ export class SpokePoolClient extends BaseAbstractClient {
}
}

if (duplicateEvents.length > 0) {
this.log("debug", "Duplicate events listed", {
duplicateEvents,
});
this.log("error", "Duplicate events detected, check debug logs");
}

// Next iteration should start off from where this one ended.
this.currentTime = currentTime;
this.firstDepositIdForSpokePool = update.firstDepositId;
Expand Down
4 changes: 4 additions & 0 deletions src/utils/EventUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,7 @@ export function isEventOlder<T extends SortableEvent>(ex: T, ey: T): boolean {
export function getTransactionHashes(events: SortableEvent[]): string[] {
return [...Array.from(new Set(events.map((e) => e.transactionHash)))];
}

export function duplicateEvent(a: SortableEvent, b: SortableEvent): boolean {
return a.transactionHash === b.transactionHash && a.logIndex === b.logIndex;
}

0 comments on commit 5bf7ac5

Please sign in to comment.