Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unintended Market Resolution Due to Incorrect Reality.eth Question Opening Time #123

Open
hats-bug-reporter bot opened this issue Sep 28, 2024 · 2 comments
Labels
bug Something isn't working invalid This doesn't seem right

Comments

@hats-bug-reporter
Copy link

Github username: --
Twitter username: --
Submission hash (on-chain): 0x4dc4a8bee494100f2103fb12adc9d2fb24a785ccbc5ec3e15d4a2e88f907c52c
Severity: medium

Description:

Details

The Seer Protocol's MarketFactory contract creates prediction markets linked to Reality.eth questions. The askRealityQuestion function is responsible for submitting these questions to Reality.eth.

The askRealityQuestion function uses a user-provided openingTime parameter when creating or reusing Reality.eth questions. However, it doesn't validate this parameter against the current block timestamp, potentially allowing the creation of markets with questions that open in the past.

Code Snippet

function askRealityQuestion(
    string memory encodedQuestion,
    uint256 templateId,
    uint32 openingTime,
    uint256 minBond
) internal returns (bytes32) {
    bytes32 content_hash = keccak256(abi.encodePacked(templateId, openingTime, encodedQuestion));
    bytes32 question_id = keccak256(
        abi.encodePacked(
            content_hash, arbitrator, questionTimeout, minBond, address(realitio), address(this), uint256(0)
        )
    );
    if (realitio.getTimeout(question_id) != 0) {
        return question_id;
    }
    return realitio.askQuestionWithMinBond(
        templateId, encodedQuestion, arbitrator, questionTimeout, openingTime, 0, minBond
    );
}

Impact

  1. Markets could be created with questions that are immediately open for answering, potentially leading to rapid and unexpected resolutions.
  2. Malicious actors could exploit this to create markets that resolve quickly based on past events, taking advantage of information asymmetry.
  3. The integrity of the prediction market system could be compromised, as markets might not provide a fair opportunity for all participants to engage.

Scenario

  1. A user creates a market with an openingTime set to a past timestamp.
  2. The Reality.eth question is immediately open for answering.
  3. The user or an accomplice quickly provides an answer to the question.
  4. The market resolves based on this answer before most participants are aware of its existence.

Probability

High. This issue can be easily triggered by any user creating a market, either accidentally or intentionally.

Fix

Implement a check in the askRealityQuestion function to ensure the openingTime is in the future:

function askRealityQuestion(
    string memory encodedQuestion,
    uint256 templateId,
    uint32 openingTime,
    uint256 minBond
) internal returns (bytes32) {
    require(openingTime > block.timestamp, "Opening time must be in the future");
    // Rest of the function remains the same
    ...
}

Consider adding an upper bound to the openingTime as well, to prevent markets from being created too far in the future.

Poc

describe("askRealityQuestion vulnerability", function () {
  it("allows creation of markets with questions opening in the past", async function () {
    const pastOpeningTime = await time.latest() - 3600; // 1 hour in the past
    const futureOpeningTime = await time.latest() + 3600; // 1 hour in the future

    // Create a market with a past opening time (this should ideally fail, but currently doesn't)
    await expect(
      marketFactory.createCategoricalMarket({
        ...categoricalMarketParams,
        openingTime: pastOpeningTime,
      })
    ).to.emit(marketFactory, "NewMarket");

    // Create a market with a future opening time (this should succeed)
    await expect(
      marketFactory.createCategoricalMarket({
        ...categoricalMarketParams,
        openingTime: futureOpeningTime,
      })
    ).to.emit(marketFactory, "NewMarket");

    // Check that both markets were created
    expect(await marketFactory.marketCount()).to.equal(2);

    // Get the created markets
    const markets = await marketFactory.allMarkets();
    const pastMarket = await ethers.getContractAt("Market", markets[0]);
    const futureMarket = await ethers.getContractAt("Market", markets[1]);

    // Check the opening times of the created questions
    const pastQuestionId = (await pastMarket.questionsIds())[0];
    const futureQuestionId = (await futureMarket.questionsIds())[0];

    const pastQuestionOpeningTime = await realitio.getOpeningTS(pastQuestionId);
    const futureQuestionOpeningTime = await realitio.getOpeningTS(futureQuestionId);

    // Both should be in the past and future respectively
    expect(pastQuestionOpeningTime).to.be.lt(await time.latest());
    expect(futureQuestionOpeningTime).to.be.gt(await time.latest());

    // Demonstrate that the past market can be answered immediately
    const answerer = (await ethers.getSigners())[1];
    await realitio.connect(answerer).submitAnswer(
      pastQuestionId,
      ethers.encodeBytes32String("1"), // Assuming "1" is a valid answer
      0, // Current bond
      { value: ethers.parseEther(MIN_BOND) }
    );

    // Check that the answer was accepted
    const lastAnswerTime = await realitio.getAnswerTimestamp(pastQuestionId);
    expect(lastAnswerTime).to.be.gt(0);
  });
});

This test does the following:

  1. It creates two markets: one with an opening time in the past and one in the future.
  2. It verifies that both markets are created successfully (which demonstrates the bug, as the past market should ideally fail).
  3. It checks the actual opening times of the Reality.eth questions for both markets.
  4. It demonstrates that the market with the past opening time can be answered immediately, which shouldn't be possible in a properly functioning system.
@hats-bug-reporter hats-bug-reporter bot added the bug Something isn't working label Sep 28, 2024
@greenlucid
Copy link

#26 (comment)

Use search function next time you submit an issue. Dupe, invalid

@clesaege
Copy link

Similar to #26 (comment)

@clesaege clesaege added the invalid This doesn't seem right label Sep 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working invalid This doesn't seem right
Projects
None yet
Development

No branches or pull requests

2 participants