Skip to content

Commit

Permalink
improve(relayer): Permit ignoring origin chains
Browse files Browse the repository at this point in the history
This is a simple relayer-specific config to permit ignoring deposits
based on their origin chain ID.

Note: This is implemented at the relayer level because the anticipated
UBA rollout will require relayers to validate events on _all_ supported
chains, not only the ones they wish to support.

Ref ACX-1516
  • Loading branch information
pxrl committed Sep 1, 2023
1 parent 561c45c commit 7357d18
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 26 deletions.
9 changes: 6 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,12 @@ NODE_MAX_CONCURRENCY=25
SEND_RELAYS=false


# List of destination chains to be supported by the relayer. If set to a
# non-empty list, only transfers going to these chains will be filled. For
# example, if set to [1], only transfers destined for Ethereum will be filled.
# List of origin and destination chains to be supported by the relayer. If set
# to a non-empty list, only transfers complying with the specified origin and
# destination chains will be filled. For example:
# RELAYER_ORIGIN_CHAINS=[1] # Only fill deposits that were placed on Optimism.
# RELAYER_DESTINATION_CHAINS=[10] # Only fill deposits destined for Ethereum.
RELAYER_ORIGIN_CHAINS=[1,10,137,324,42161]
RELAYER_DESTINATION_CHAINS=[1,10,137,42161]


Expand Down
42 changes: 28 additions & 14 deletions src/relayer/Relayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,20 @@ export class Relayer {
return supportedDeposits;
}

routeEnabled(originChainId: number, destinationChainId: number): boolean {
const { relayerOriginChains: originChains, relayerDestinationChains: destinationChains } = this.config;

if (originChains?.length > 0 && !originChains.includes(originChainId)) {
return false;
}

if (destinationChains?.length > 0 && !destinationChains.includes(destinationChainId)) {
return false;
}

return true;
}

async checkForUnfilledDepositsAndFill(sendSlowRelays = true): Promise<void> {
// Fetch all unfilled deposits, order by total earnable fee.
const { config } = this;
Expand Down Expand Up @@ -138,11 +152,23 @@ export class Relayer {
// is has no other fills then send a 0 sized fill to initiate a slow relay. If unprofitable then add the
// unprofitable tx to the unprofitable tx tracker to produce an appropriate log.
for (const { deposit, version, unfilledAmount, fillCount, invalidFills } of confirmedUnfilledDeposits) {
const { relayerDestinationChains, relayerTokens, slowDepositors } = config;
const { relayerTokens, slowDepositors } = config;

const { originChainId, destinationChainId, originToken } = deposit;
const destinationChain = getNetworkName(destinationChainId);

if (!this.routeEnabled(originChainId, destinationChainId)) {
this.logger.debug({
at: "Relayer",
message: "Skipping deposit for unsupported origin or destination chain",
deposit,
});
continue;
}

// Skip any L1 tokens that are not specified in the config.
// If relayerTokens is an empty list, we'll assume that all tokens are supported.
const l1Token = hubPoolClient.getL1TokenInfoForL2Token(deposit.originToken, deposit.originChainId);
const l1Token = hubPoolClient.getL1TokenInfoForL2Token(originToken, originChainId);
if (
relayerTokens.length > 0 &&
!relayerTokens.includes(l1Token.address) &&
Expand All @@ -152,18 +178,6 @@ export class Relayer {
continue;
}

const destinationChainId = deposit.destinationChainId;
const destinationChain = getNetworkName(destinationChainId);
if (relayerDestinationChains.length > 0 && !relayerDestinationChains.includes(destinationChainId)) {
this.logger.debug({
at: "Relayer",
message: "Skipping deposit for unsupported destination chain",
deposit,
destinationChain,
});
continue;
}

// Skip deposits that contain invalid fills from the same relayer. This prevents potential corrupted data from
// making the same relayer fill a deposit multiple times.
if (!config.acceptInvalidFills && invalidFills.some((fill) => fill.relayer === this.relayerAddress)) {
Expand Down
8 changes: 6 additions & 2 deletions src/relayer/RelayerConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export class RelayerConfig extends CommonConfig {
readonly sendingSlowRelaysEnabled: boolean;
readonly sendingRefundRequestsEnabled: boolean;
readonly relayerTokens: string[];
readonly relayerDestinationChains: number[];
readonly relayerOriginChains: number[] = [];
readonly relayerDestinationChains: number[] = [];
readonly relayerGasMultiplier: BigNumber;
readonly minRelayerFeePct: BigNumber;
readonly acceptInvalidFills: boolean;
Expand All @@ -35,6 +36,7 @@ export class RelayerConfig extends CommonConfig {

constructor(env: ProcessEnv) {
const {
RELAYER_ORIGIN_CHAINS,
RELAYER_DESTINATION_CHAINS,
SLOW_DEPOSITORS,
DEBUG_PROFITABILITY,
Expand All @@ -53,7 +55,9 @@ export class RelayerConfig extends CommonConfig {
super(env);

// Empty means all chains.
this.relayerDestinationChains = RELAYER_DESTINATION_CHAINS ? JSON.parse(RELAYER_DESTINATION_CHAINS) : [];
this.relayerOriginChains = JSON.parse(RELAYER_ORIGIN_CHAINS ?? "[]");
this.relayerDestinationChains = JSON.parse(RELAYER_DESTINATION_CHAINS ?? "[]");

// Empty means all tokens.
this.relayerTokens = RELAYER_TOKENS
? JSON.parse(RELAYER_TOKENS).map((token) => ethers.utils.getAddress(token))
Expand Down
22 changes: 15 additions & 7 deletions test/Relayer.BasicFill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,6 @@ describe("Relayer: Check for Unfilled Deposits and Fill", async function () {
},
{
relayerTokens: [],
relayerDestinationChains: [originChainId, destinationChainId],
minDepositConfirmations: defaultMinDepositConfirmations,
quoteTimeBuffer: 0,
} as unknown as RelayerConfig
Expand Down Expand Up @@ -233,7 +232,6 @@ describe("Relayer: Check for Unfilled Deposits and Fill", async function () {
},
{
relayerTokens: [],
relayerDestinationChains: [originChainId, destinationChainId],
minDepositConfirmations: {
default: { [originChainId]: 10 }, // This needs to be set large enough such that the deposit is ignored.
},
Expand Down Expand Up @@ -270,7 +268,6 @@ describe("Relayer: Check for Unfilled Deposits and Fill", async function () {
},
{
relayerTokens: [],
relayerDestinationChains: [originChainId, destinationChainId],
minDepositConfirmations: defaultMinDepositConfirmations,
quoteTimeBuffer: 100,
sendingRelaysEnabled: false,
Expand Down Expand Up @@ -421,7 +418,7 @@ describe("Relayer: Check for Unfilled Deposits and Fill", async function () {
expect(multiCallerClient.transactionCount()).to.equal(1); // no Transactions to send.
});

it("Skip unwhitelisted chains", async function () {
it("Respects configured relayer routes", async function () {
relayerInstance = new Relayer(
relayer.address,
spyLogger,
Expand All @@ -438,20 +435,31 @@ describe("Relayer: Check for Unfilled Deposits and Fill", async function () {
},
{
relayerTokens: [],
relayerOriginChains: [destinationChainId],
relayerDestinationChains: [originChainId],
minDepositConfirmations: defaultMinDepositConfirmations,
quoteTimeBuffer: 0,
} as unknown as RelayerConfig
);

// Test the underlying route validation logic.
const routes = [
{ from: originChainId, to: destinationChainId, enabled: false },
{ from: originChainId, to: originChainId, enabled: false },
{ from: destinationChainId, to: originChainId, enabled: true },
{ from: destinationChainId, to: destinationChainId, enabled: false },
];
routes.forEach(({ from, to, enabled }) => expect(relayerInstance.routeEnabled(from, to)).to.equal(enabled));

// Verify that the relayer adheres to route validation.
// Deposit is not on a whitelisted destination chain so relayer shouldn't fill it.
await spokePool_1.setCurrentTime(await getLastBlockTime(spokePool_1.provider));
await deposit(spokePool_1, erc20_1, depositor, depositor, destinationChainId);

// Check that no transaction was sent.
// Deposit on originChainId, destined for destinationChainId => expect ignored.
await deposit(spokePool_1, erc20_1, depositor, depositor, destinationChainId);
await updateAllClients();
await relayerInstance.checkForUnfilledDepositsAndFill();
expect(lastSpyLogIncludes(spy, "Skipping deposit for unsupported destination chain")).to.be.true;
expect(lastSpyLogIncludes(spy, "Skipping deposit for unsupported origin or destination chain")).to.be.true;
});

it("UBA: Doesn't crash if client cannot support version bump", async function () {
Expand Down

0 comments on commit 7357d18

Please sign in to comment.