NomicFoundation / hardhat

Hardhat is a development environment to compile, deploy, test, and debug your Ethereum software.
https://hardhat.org
Other
7.06k stars 1.36k forks source link

`hardhat_mine` doesn't work with large block numbers #3006

Open wchargin opened 1 year ago

wchargin commented 1 year ago

I have a contract that behaves differently when the block number exceeds 2^64. Obviously, this height isn't expected to be reached any time soon under current mainnet parameters, but it could plausibly happen on some side chain (or later version of mainnet), and it should definitely be possible to test.

At first glance, the hardhat_mine RPC from #1112 and #2032 seem like the perfect tools. But I'm running into two issues:

This script demonstrates (npx hardhat run script.js in a fresh repo):

const hre = require("hardhat");
const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");

async function deployFixture() {
  // Nothing to do.
}

async function go(exponent) {
  const n = 1n << BigInt(exponent);
  console.log(`--- n = 2^${exponent}`);
  try {
    await loadFixture(deployFixture);
    const hexN = "0x" + n.toString(16);
    const start = process.hrtime.bigint();
    await hre.network.provider.send("hardhat_mine", [hexN]);
    console.log(`mined ${n} blocks`);
    const blockNumber = await hre.network.provider
      .send("eth_blockNumber")
      .then((n) => hre.ethers.BigNumber.from(n));
    const end = process.hrtime.bigint();
    console.log(`latest block: ${blockNumber}`);
    console.log(`elapsed: ${formatNs(end - start)}`);
  } catch (e) {
    console.error("failed: " + e);
  }
  console.log();
}

function formatNs(ns) {
  const us = Number(ns / BigInt(1e3));
  const ms = (us / 1e3).toFixed(3);
  return `${ms} ms`;
}

async function main() {
  await go(0);
  await go(64); // fails: "Number can only safely store up to 53 bits"

  await go(1);
  await go(16);
  await go(32);
  await go(34);
  await go(36);
  await go(37);
  await go(38);
  await go(40);
}

main();

Sample outputs:

--- n = 2^0
mined 1 blocks
latest block: 1
elapsed: 46.037 ms

--- n = 2^64
failed: Error: Number can only safely store up to 53 bits

--- n = 2^1
mined 2 blocks
latest block: 2
elapsed: 3.287 ms

--- n = 2^16
mined 65536 blocks
latest block: 65536
elapsed: 4.223 ms

--- n = 2^32
mined 4294967296 blocks
latest block: 4294967296
elapsed: 31.722 ms

--- n = 2^34
mined 17179869184 blocks
latest block: 17179869184
elapsed: 185.327 ms

--- n = 2^36
mined 68719476736 blocks
latest block: 68719476736
elapsed: 2770.086 ms

--- n = 2^37
mined 137438953472 blocks
latest block: 137438953472
elapsed: 10960.585 ms

--- n = 2^38
mined 274877906944 blocks
latest block: 274877906944
elapsed: 44671.872 ms

--- n = 2^40
^C [after 10 minutes]

Also, executing trivial transactions after mining order-of 2^38 blocks seems to take a long time, too (order-of 100 seconds).

This is a little surprising to me since the mineBlocks implementation seems to take its arguments as BigNumbers, so it sounds like it expects to work in those ranges: https://github.com/NomicFoundation/hardhat/blob/bcd32259a1439a57a60ca67d2682014d051f5f8a/packages/hardhat-core/src/internal/hardhat-network/provider/node.ts#L508-L509

What's the best way for me to test contract behavior when block.number exceeds type(uint64).max?

github-actions[bot] commented 1 year ago

This issue is also being tracked on Linear.

We use Linear to manage our development process, but we keep the conversations on Github.

LINEAR-ID: 54194f1e-982c-497c-8bfa-3925d5a114ea

feuGeneA commented 1 year ago

We're giving this a lower priority because it doesn't seem to have a real world application/impact. If you disagree, please explain your use case and it may convince us to increase the priority. Thank you.