Skip to content

Commit

Permalink
fix(monitor-v2): check osnap replay attacks (#4588)
Browse files Browse the repository at this point in the history
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
  • Loading branch information
Reinis-FRP authored Jul 11, 2023
1 parent f1854ba commit 29e82c3
Showing 1 changed file with 39 additions and 2 deletions.
41 changes: 39 additions & 2 deletions packages/monitor-v2/src/monitor-og/SnapshotVerification.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { TransactionsProposedEvent } from "@uma/contracts-node/typechain/core/ethers/OptimisticGovernor";
import {
ProposalExecutedEvent,
TransactionsProposedEvent,
} from "@uma/contracts-node/typechain/core/ethers/OptimisticGovernor";
import assert from "assert";
import retry, { Options as RetryOptions } from "async-retry";
import { BigNumber, utils as ethersUtils } from "ethers";
import fetch from "node-fetch";
import { request } from "graphql-request";
import { gql } from "graphql-tag";

import { isDictionary, MonitoringParams, tryHexToUtf8String } from "./common";
import { isDictionary, getOgByAddress, MonitoringParams, runQueryFilter, tryHexToUtf8String } from "./common";

// If there are multiple transactions within a batch, they are aggregated as multiSend in the mainTransaction.
interface MainTransaction {
Expand Down Expand Up @@ -422,6 +425,36 @@ export const verifyRules = (parsedRules: RulesParameters, proposal: SnapshotProp
return { verified: true };
};

// Check if the proposal has been executed before.
const hasBeenExecuted = async (
currentProposal: TransactionsProposedEvent,
params: MonitoringParams
): Promise<boolean> => {
// Get all other proposals with matching transactions and explanation for the same module that were proposed till the
// the current proposal's block number. Matching proposals will include the current proposal, but we know that it
// cannot be executed in the same block as liveness cannot be 0.
const og = await getOgByAddress(params, currentProposal.address);
const matchingProposals = (
await runQueryFilter<TransactionsProposedEvent>(og, og.filters.TransactionsProposed(), {
start: 0,
end: currentProposal.blockNumber,
})
).filter(
(otherProposal) =>
otherProposal.args.proposalHash === currentProposal.args.proposalHash &&
otherProposal.args.explanation === currentProposal.args.explanation
);

// Return true if any of the matching proposals have been executed.
const executedAssertionIds = (
await runQueryFilter<ProposalExecutedEvent>(og, og.filters.ProposalExecuted(), {
start: 0,
end: currentProposal.blockNumber,
})
).map((executedProposal) => executedProposal.args.assertionId);
return matchingProposals.some((matchingProposal) => executedAssertionIds.includes(matchingProposal.args.assertionId));
};

export const verifyProposal = async (
transaction: TransactionsProposedEvent,
params: MonitoringParams
Expand Down Expand Up @@ -484,6 +517,10 @@ export const verifyProposal = async (
const rulesVerification = verifyRules(parsedRules, proposal);
if (!rulesVerification.verified) return rulesVerification;

// Verify that the same proposal has not been executed before.
if (await hasBeenExecuted(transaction, params))
return { verified: false, error: "Proposal has been executed before" };

// All checks passed.
return { verified: true };
};

0 comments on commit 29e82c3

Please sign in to comment.