Skip to content

Commit

Permalink
refactor: Update Monitor test to v3 (#1232)
Browse files Browse the repository at this point in the history
While here, remove the unknown relayer reporting feature. This has been
disabled in prod for quite a while, and given that there are so many
active relayers now there's no apparent use case for it.
  • Loading branch information
pxrl authored Feb 26, 2024
1 parent 1ed83a7 commit 88e103b
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 125 deletions.
23 changes: 0 additions & 23 deletions src/monitor/Monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,29 +177,6 @@ export class Monitor {
}
}

async checkUnknownRelayers(): Promise<void> {
const chainIds = this.monitorChains;
this.logger.debug({ at: "Monitor#checkUnknownRelayers", message: "Checking for unknown relayers", chainIds });
for (const chainId of chainIds) {
const fills = this.clients.spokePoolClients[chainId].getFillsWithBlockInRange(
this.spokePoolsBlocks[chainId].startingBlock,
this.spokePoolsBlocks[chainId].endingBlock
);
for (const fill of fills) {
// Skip notifications for known relay caller addresses, or slow fills.
const isSlowRelay = sdkUtils.isSlowFill(fill);
if (this.monitorConfig.whitelistedRelayers.includes(fill.relayer) || isSlowRelay) {
continue;
}

const mrkdwn =
`An unknown relayer ${blockExplorerLink(fill.relayer, chainId)}` +
` filled a deposit on ${getNetworkName(chainId)}\ntx: ${blockExplorerLink(fill.transactionHash, chainId)}`;
this.logger.warn({ at: "Monitor#checkUnknownRelayers", message: "Unknown relayer 🛺", mrkdwn });
}
}
}

async reportUnfilledDeposits(): Promise<void> {
const unfilledDeposits = await getUnfilledDeposits(
this.clients.spokePoolClients,
Expand Down
3 changes: 0 additions & 3 deletions src/monitor/MonitorConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export interface BotModes {
stuckRebalancesEnabled: boolean;
utilizationEnabled: boolean; // Monitors pool utilization ratio
unknownRootBundleCallersEnabled: boolean; // Monitors relay related events triggered by non-whitelisted addresses
unknownRelayerCallersEnabled: boolean;
}

export class MonitorConfig extends CommonConfig {
Expand Down Expand Up @@ -50,7 +49,6 @@ export class MonitorConfig extends CommonConfig {
MONITOR_REPORT_ENABLED,
UTILIZATION_ENABLED,
UNKNOWN_ROOT_BUNDLE_CALLERS_ENABLED,
UNKNOWN_RELAYER_CALLERS_ENABLED,
UTILIZATION_THRESHOLD,
WHITELISTED_DATA_WORKERS,
WHITELISTED_RELAYERS,
Expand All @@ -68,7 +66,6 @@ export class MonitorConfig extends CommonConfig {
reportEnabled: MONITOR_REPORT_ENABLED === "true",
utilizationEnabled: UTILIZATION_ENABLED === "true",
unknownRootBundleCallersEnabled: UNKNOWN_ROOT_BUNDLE_CALLERS_ENABLED === "true",
unknownRelayerCallersEnabled: UNKNOWN_RELAYER_CALLERS_ENABLED === "true",
stuckRebalancesEnabled: STUCK_REBALANCES_ENABLED === "true",
};

Expand Down
6 changes: 0 additions & 6 deletions src/monitor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,6 @@ export async function runMonitor(_logger: winston.Logger, baseSigner: Signer): P
logger.debug({ at: "Monitor#index", message: "UnknownRootBundleCallers monitor disabled" });
}

if (config.botModes.unknownRelayerCallersEnabled) {
await acrossMonitor.checkUnknownRelayers();
} else {
logger.debug({ at: "Monitor#index", message: "UnknownRelayerCallers monitor disabled" });
}

if (config.botModes.stuckRebalancesEnabled) {
await acrossMonitor.checkStuckRebalances();
} else {
Expand Down
174 changes: 81 additions & 93 deletions test/Monitor.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { utils as sdkUtils } from "@across-protocol/sdk-v2";
import {
BalanceAllocator,
BundleDataClient,
Expand All @@ -10,52 +11,75 @@ import {
import { CrossChainTransferClient } from "../src/clients/bridges";
import { spokePoolClientsToProviders } from "../src/common";
import { Dataworker } from "../src/dataworker/Dataworker";
import { BalanceType } from "../src/interfaces";
import { BalanceType, V3DepositWithBlock } from "../src/interfaces";
import {
ALL_CHAINS_NAME,
Monitor,
REBALANCE_FINALIZE_GRACE_PERIOD,
UNKNOWN_TRANSFERS_NAME,
} from "../src/monitor/Monitor";
import { MonitorConfig } from "../src/monitor/MonitorConfig";
import { MAX_UINT_VAL, getNetworkName, getRefundForFills, toBN } from "../src/utils";
import { MAX_UINT_VAL, getNetworkName, toBN } from "../src/utils";
import * as constants from "./constants";
import { amountToDeposit, destinationChainId, mockTreeRoot, originChainId, repaymentChainId } from "./constants";
import { setupDataworker } from "./fixtures/Dataworker.Fixture";
import { MockAdapterManager } from "./mocks";
import {
BigNumber,
Contract,
SignerWithAddress,
buildDeposit,
buildFillForRepaymentChain,
createSpyLogger,
depositV3,
fillV3Relay,
ethers,
expect,
lastSpyLogIncludes,
toBNWei,
} from "./utils";

let l1Token: Contract, l2Token: Contract, erc20_2: Contract;
let hubPool: Contract, spokePool_1: Contract, spokePool_2: Contract;
let dataworker: SignerWithAddress, depositor: SignerWithAddress;
let dataworkerInstance: Dataworker;
let bundleDataClient: BundleDataClient;
let configStoreClient: ConfigStoreClient;
let hubPoolClient: HubPoolClient, multiCallerClient: MultiCallerClient;
let tokenTransferClient: TokenTransferClient;
let monitorInstance: Monitor;
let spokePoolClients: { [chainId: number]: SpokePoolClient };
let crossChainTransferClient: CrossChainTransferClient;
let adapterManager: MockAdapterManager;
let updateAllClients: () => Promise<void>;

const { spy, spyLogger } = createSpyLogger();

const TEST_NETWORK_NAMES = ["Hardhat1", "Hardhat2", "unknown", ALL_CHAINS_NAME];
describe("Monitor", async function () {
const TEST_NETWORK_NAMES = ["Hardhat1", "Hardhat2", "unknown", ALL_CHAINS_NAME];
let l1Token: Contract, l2Token: Contract, erc20_2: Contract;
let hubPool: Contract, spokePool_1: Contract, spokePool_2: Contract;
let dataworker: SignerWithAddress, depositor: SignerWithAddress;
let dataworkerInstance: Dataworker;
let bundleDataClient: BundleDataClient;
let configStoreClient: ConfigStoreClient;
let hubPoolClient: HubPoolClient, multiCallerClient: MultiCallerClient;
let tokenTransferClient: TokenTransferClient;
let monitorInstance: Monitor;
let spokePoolClients: { [chainId: number]: SpokePoolClient };
let crossChainTransferClient: CrossChainTransferClient;
let adapterManager: MockAdapterManager;
let defaultMonitorEnvVars: Record<string, string>;
let updateAllClients: () => Promise<void>;
const { spy, spyLogger } = createSpyLogger();

const executeBundle = async (hubPool: Contract) => {
const latestBlock = await hubPool.provider.getBlockNumber();
const blockRange = constants.CHAIN_ID_TEST_LIST.map(() => [0, latestBlock]);
const expectedPoolRebalanceRoot = await dataworkerInstance.buildPoolRebalanceRoot(blockRange, spokePoolClients);
await hubPool.setCurrentTime(Number(await hubPool.getCurrentTime()) + Number(await hubPool.liveness()) + 1);
for (const leaf of expectedPoolRebalanceRoot.leaves) {
await hubPool.executeRootBundle(
leaf.chainId,
leaf.groupIndex,
leaf.bundleLpFees,
leaf.netSendAmounts,
leaf.runningBalances,
leaf.leafId,
leaf.l1Tokens,
expectedPoolRebalanceRoot.tree.getHexProof(leaf)
);
}
};

let defaultMonitorEnvVars;
const { fixedPointAdjustment: fixedPoint } = sdkUtils;
const computeRelayerRefund = async (request: V3DepositWithBlock & { paymentChainId: number }): Promise<BigNumber> => {
const { realizedLpFeePct } = await hubPoolClient.computeRealizedLpFeePct(request);
return request.inputAmount.mul(fixedPoint.sub(realizedLpFeePct)).div(fixedPoint);
};

describe("Monitor", async function () {
beforeEach(async function () {
({
configStoreClient,
Expand Down Expand Up @@ -105,11 +129,7 @@ describe("Monitor", async function () {
const chainIds = [hubPoolClient.chainId, repaymentChainId, originChainId, destinationChainId];
bundleDataClient = new BundleDataClient(
spyLogger,
{
configStoreClient,
multiCallerClient,
hubPoolClient,
},
{ configStoreClient, multiCallerClient, hubPoolClient },
spokePoolClients,
chainIds
);
Expand Down Expand Up @@ -161,30 +181,6 @@ describe("Monitor", async function () {
expect(lastSpyLogIncludes(spy, unknownDisputerMessage)).to.be.true;
});

it("Monitor should log unknown relayers", async function () {
await updateAllClients();
await monitorInstance.update();
await monitorInstance.checkUnknownRelayers();
const unknownRelayerMessage = "Unknown relayer 🛺";
expect(lastSpyLogIncludes(spy, unknownRelayerMessage)).to.be.false;

// Send a deposit and a fill so that dataworker builds simple roots.
const deposit = await buildDeposit(
hubPoolClient,
spokePool_1,
l2Token,
l1Token,
depositor,
destinationChainId,
amountToDeposit
);
await buildFillForRepaymentChain(spokePool_2, depositor, deposit, 0.5, constants.destinationChainId);

await monitorInstance.update();
await monitorInstance.checkUnknownRelayers();
expect(lastSpyLogIncludes(spy, unknownRelayerMessage)).to.be.true;
});

it("Monitor should report balances", async function () {
await monitorInstance.update();
const reports = monitorInstance.initializeBalanceReports(
Expand All @@ -203,16 +199,18 @@ describe("Monitor", async function () {
await updateAllClients();
await monitorInstance.update();
// Send a deposit and a fill so that dataworker builds simple roots.
const deposit = await buildDeposit(
hubPoolClient,
const inputAmount = amountToDeposit;
const outputAmount = inputAmount.mul(99).div(100);
const deposit = await depositV3(
spokePool_1,
l2Token,
l1Token,
depositor,
destinationChainId,
amountToDeposit
depositor,
l2Token.address,
inputAmount,
l1Token.address,
outputAmount
);
const fill1 = await buildFillForRepaymentChain(spokePool_2, depositor, deposit, 0.5, destinationChainId);
const fill = await fillV3Relay(spokePool_2, deposit, depositor);
await monitorInstance.update();

// Have the data worker propose a new bundle.
Expand All @@ -230,9 +228,9 @@ describe("Monitor", async function () {
);
await monitorInstance.updateLatestAndFutureRelayerRefunds(reports);
expect(reports[depositor.address]["L1Token1"][ALL_CHAINS_NAME][BalanceType.PENDING]).to.be.equal(toBN(0));
expect(reports[depositor.address]["L1Token1"][ALL_CHAINS_NAME][BalanceType.NEXT]).to.be.equal(
getRefundForFills([fill1])
);

const relayerRefund = await computeRelayerRefund({ ...deposit, paymentChainId: fill.repaymentChainId });
expect(reports[depositor.address]["L1Token1"][ALL_CHAINS_NAME][BalanceType.NEXT]).to.be.equal(relayerRefund);

// Execute pool rebalance leaves.
await executeBundle(hubPool);
Expand All @@ -247,9 +245,7 @@ describe("Monitor", async function () {
);
await monitorInstance.updateLatestAndFutureRelayerRefunds(reports);
expect(reports[depositor.address]["L1Token1"][ALL_CHAINS_NAME][BalanceType.NEXT]).to.be.equal(toBN(0));
expect(reports[depositor.address]["L1Token1"][ALL_CHAINS_NAME][BalanceType.PENDING]).to.be.equal(
getRefundForFills([fill1])
);
expect(reports[depositor.address]["L1Token1"][ALL_CHAINS_NAME][BalanceType.PENDING]).to.be.equal(relayerRefund);

// Manually relay the roots to spoke pools since adapter is a dummy and won't actually relay messages.
const validatedRootBundles = hubPoolClient.getValidatedRootBundles();
Expand All @@ -260,7 +256,7 @@ describe("Monitor", async function () {
await updateAllClients();

// Execute relayer refund leaves. Send funds to spoke pools to execute the leaves.
await erc20_2.mint(spokePool_2.address, getRefundForFills([fill1]));
await erc20_2.mint(spokePool_2.address, relayerRefund);
const providers = {
...spokePoolClientsToProviders(spokePoolClients),
[(await hubPool.provider.getNetwork()).chainId]: hubPool.provider,
Expand Down Expand Up @@ -297,16 +293,16 @@ describe("Monitor", async function () {
await updateAllClients();
await monitorInstance.update();
// Send a deposit and a fill so that dataworker builds simple roots.
const deposit = await buildDeposit(
hubPoolClient,
const deposit = await depositV3(
spokePool_1,
l2Token,
l1Token,
depositor,
destinationChainId,
amountToDeposit
depositor,
l2Token.address,
amountToDeposit,
l1Token.address,
amountToDeposit.mul(99).div(100)
);
await buildFillForRepaymentChain(spokePool_2, depositor, deposit, 0.5, destinationChainId);
await fillV3Relay(spokePool_2, deposit, depositor);
await monitorInstance.update();

// Have the data worker propose a new bundle.
Expand Down Expand Up @@ -341,7 +337,18 @@ describe("Monitor", async function () {
it("Monitor should report unfilled deposits", async function () {
await updateAllClients();
await monitorInstance.update();
await buildDeposit(hubPoolClient, spokePool_1, l2Token, l1Token, depositor, destinationChainId, amountToDeposit);
const inputToken = l2Token.address;
const outputToken = erc20_2.address;
await depositV3(
spokePool_1,
destinationChainId,
depositor,
inputToken,
amountToDeposit,
outputToken,
amountToDeposit
);

await monitorInstance.update();
await monitorInstance.reportUnfilledDeposits();

Expand Down Expand Up @@ -407,22 +414,3 @@ describe("Monitor", async function () {
expect(await spokePool_1.provider.getBalance(spokePool_1.address)).to.equal(toBNWei("2"));
});
});

const executeBundle = async (hubPool: Contract) => {
const latestBlock = await hubPool.provider.getBlockNumber();
const blockRange = constants.CHAIN_ID_TEST_LIST.map(() => [0, latestBlock]);
const expectedPoolRebalanceRoot = await dataworkerInstance.buildPoolRebalanceRoot(blockRange, spokePoolClients);
await hubPool.setCurrentTime(Number(await hubPool.getCurrentTime()) + Number(await hubPool.liveness()) + 1);
for (const leaf of expectedPoolRebalanceRoot.leaves) {
await hubPool.executeRootBundle(
leaf.chainId,
leaf.groupIndex,
leaf.bundleLpFees,
leaf.netSendAmounts,
leaf.runningBalances,
leaf.leafId,
leaf.l1Tokens,
expectedPoolRebalanceRoot.tree.getHexProof(leaf)
);
}
};

0 comments on commit 88e103b

Please sign in to comment.