Skip to content

Commit

Permalink
feat: added tests when shomei-frontend or l2-node shut and comes back up
Browse files Browse the repository at this point in the history
  • Loading branch information
Julink-eth committed Jul 24, 2024
1 parent cd40e58 commit 164a1b6
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 42 deletions.
181 changes: 148 additions & 33 deletions packages/linea-ens-resolver/test/testL1ResolverLocal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import { EthereumProvider } from "hardhat/types";
import {
changeBlockNumberInCCIPResponse,
deployContract,
execDockerCommand,
fetchCCIPGateway,
getAndIncreaseFeeData,
getExtraData,
sendTransactionsWithInterval,
waitForL2BlockNumberFinalized,
waitForLatestL2BlockNumberFinalizedToChange,
} from "./utils";
import { setTimeout } from "timers/promises";

const labelhash = (label) => ethers.keccak256(ethers.toUtf8Bytes(label));
const encodeName = (name) => "0x" + packet.name.encode(name).toString("hex");
Expand All @@ -37,13 +39,20 @@ const SIGNER_L1_PK =
// Account 1 on L2 "FOR LOCAL DEV ONLY - DO NOT REUSE THESE KEYS ELSEWHERE"
const SIGNER_L2_PK =
"8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63";
// Account 2 on L2 "FOR LOCAL DEV ONLY - DO NOT REUSE THESE KEYS ELSEWHERE"
const SIGNER_L2_2_PK =
"c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3";

const REGISTRANT_ADDR = "0xFE3B557E8Fb62b89F4916B721be55cEb828dBd73";

const SUB_DOMAIN = "testpoh.linea-test.eth";
const subDomainNode = ethers.namehash(SUB_DOMAIN);
const encodedSubDomain = encodeName(SUB_DOMAIN);

const SUB_SUB_DOMAIN = "foo.testpoh.linea-test.eth";
const subSubNode = ethers.namehash(SUB_SUB_DOMAIN);
const encodedSubSubDomain = encodeName(SUB_SUB_DOMAIN);

const EMPTY_ADDRESS = "0x0000000000000000000000000000000000000000";
const EMPTY_BYTES32 =
"0x0000000000000000000000000000000000000000000000000000000000000000";
Expand Down Expand Up @@ -81,13 +90,14 @@ describe("Crosschain Resolver Local", () => {
let wrapper: Contract;
let baseRegistrar: Contract;
let rollup: Contract;
let l2factoryContract: Contract;
let signerL1,
signerL2,
signerL1Address,
signerL2Address,
l2ResolverAddress,
wrapperAddress;
let lastSetupTxBlockNumber: bigint;
let lastSetupTxBlockNumber = BigInt(0);
let sendTransactionsPromise: NodeJS.Timeout;

before(async () => {
Expand Down Expand Up @@ -230,7 +240,7 @@ describe("Crosschain Resolver Local", () => {
await verifier.waitForDeployment();

const implContract = await deployContract("DelegatableResolver", signerL2);
const l2factoryContract = await deployContract(
l2factoryContract = await deployContract(
"DelegatableResolverFactory",
signerL2,
await implContract.getAddress()
Expand All @@ -249,7 +259,7 @@ describe("Crosschain Resolver Local", () => {
await ens.getAddress(),
wrapperAddress,
"",
59141
L2_CHAIN_ID
);

const delegatableResolverImpl = await ethers.getContractFactory(
Expand All @@ -272,8 +282,16 @@ describe("Crosschain Resolver Local", () => {
.setText(subDomainNode, "name", "test.eth")
.then((tx) => tx.wait());

const tx = await l2Resolver.setContenthash(subDomainNode, contenthash);
await l2Resolver
.setContenthash(subDomainNode, contenthash)
.then((tx) => tx.wait());

const tx = await l2Resolver["setAddr(bytes32,address)"](
subSubNode,
REGISTRANT_ADDR
);
const txReceipt = await tx.wait();
// Kepp track of the last block tx's setup block number
lastSetupTxBlockNumber = BigInt(txReceipt.blockNumber);

// Generate activity on Linea to make finalization events happen
Expand All @@ -293,18 +311,7 @@ describe("Crosschain Resolver Local", () => {
});

it("should revert when querying L1Resolver and the currentL2BlockNumber is older than the L2 block number we are fetching the data from", async () => {
// This test needs at least one finalized L2 block
await waitForLatestL2BlockNumberFinalizedToChange(
rollup,
2000,
"finalized"
);
const currentL2BlockNumberFinalized = await rollup.currentL2BlockNumber({
blockTag: "finalized",
});
expect(lastSetupTxBlockNumber).to.be.greaterThan(
currentL2BlockNumberFinalized
);
await waitForL2BlockNumberFinalized(rollup, BigInt(1), 2000);
await target
.setTarget(encodedname, l2ResolverAddress)
.then((tx) => tx.wait());
Expand Down Expand Up @@ -390,25 +397,23 @@ describe("Crosschain Resolver Local", () => {
await target
.setTarget(encodedname, l2ResolverAddress)
.then((tx) => tx.wait());
const result = await l2Resolver["addr(bytes32)"](node);
expect(result).to.equal(EMPTY_ADDRESS);

const i = new ethers.Interface(["function addr(bytes32) returns(address)"]);
const calldata = i.encodeFunctionData("addr", [node]);
const result2 = await target.resolve(encodedname, calldata, {
const result = await target.resolve(encodedname, calldata, {
enableCcipRead: true,
});
const decoded = i.decodeFunctionResult("addr", result2);
const decoded = i.decodeFunctionResult("addr", result);
expect(decoded[0]).to.equal(EMPTY_ADDRESS);
});

it("should resolve ETH Address", async () => {
const i = new ethers.Interface(["function addr(bytes32) returns(address)"]);
const calldata = i.encodeFunctionData("addr", [subDomainNode]);
const result2 = await target.resolve(encodedSubDomain, calldata, {
const result = await target.resolve(encodedSubDomain, calldata, {
enableCcipRead: true,
});
const decoded = i.decodeFunctionResult("addr", result2);
const decoded = i.decodeFunctionResult("addr", result);
expect(ethers.getAddress(decoded[0])).to.equal(
ethers.getAddress(REGISTRANT_ADDR)
);
Expand All @@ -419,10 +424,10 @@ describe("Crosschain Resolver Local", () => {
"function addr(bytes32,uint256) returns(bytes)",
]);
const calldata = i.encodeFunctionData("addr", [subDomainNode, coinType]);
const result2 = await target.resolve(encodedSubDomain, calldata, {
const result = await target.resolve(encodedSubDomain, calldata, {
enableCcipRead: true,
});
const decoded = i.decodeFunctionResult("addr", result2);
const decoded = i.decodeFunctionResult("addr", result);
expect(decoded[0]).to.equal(testAddr);
});

Expand All @@ -431,22 +436,33 @@ describe("Crosschain Resolver Local", () => {
"function text(bytes32,string) returns(string)",
]);
const calldata = i.encodeFunctionData("text", [subDomainNode, "name"]);
const result2 = await target.resolve(encodedSubDomain, calldata, {
const result = await target.resolve(encodedSubDomain, calldata, {
enableCcipRead: true,
});
const decoded = i.decodeFunctionResult("text", result2);
const decoded = i.decodeFunctionResult("text", result);
expect(decoded[0]).to.equal("test.eth");
});

it("should resolve ETH Address for sub sub domain", async () => {
const i = new ethers.Interface(["function addr(bytes32) returns(address)"]);
const calldata = i.encodeFunctionData("addr", [subSubNode]);

const result = await target.resolve(encodedSubSubDomain, calldata, {
enableCcipRead: true,
});
const decoded = i.decodeFunctionResult("addr", result);
expect(decoded[0]).to.equal(REGISTRANT_ADDR);
});

it("should resolve contenthash", async () => {
const i = new ethers.Interface([
"function contenthash(bytes32) returns(bytes)",
]);
const calldata = i.encodeFunctionData("contenthash", [subDomainNode]);
const result2 = await target.resolve(encodedSubDomain, calldata, {
const result = await target.resolve(encodedSubDomain, calldata, {
enableCcipRead: true,
});
const decoded = i.decodeFunctionResult("contenthash", result2);
const decoded = i.decodeFunctionResult("contenthash", result);
expect(decoded[0]).to.equal(contenthash);
});

Expand Down Expand Up @@ -483,10 +499,10 @@ describe("Crosschain Resolver Local", () => {

const i = new ethers.Interface(["function addr(bytes32) returns(address)"]);
const calldata = i.encodeFunctionData("addr", [subDomainNode]);
const result2 = await target.resolve(encodedSubDomain, calldata, {
const result = await target.resolve(encodedSubDomain, calldata, {
enableCcipRead: true,
});
const decoded = i.decodeFunctionResult("addr", result2);
const decoded = i.decodeFunctionResult("addr", result);
expect(ethers.getAddress(decoded[0])).to.equal(
ethers.getAddress(REGISTRANT_ADDR)
);
Expand All @@ -501,10 +517,10 @@ describe("Crosschain Resolver Local", () => {

const i = new ethers.Interface(["function addr(bytes32) returns(address)"]);
const calldata = i.encodeFunctionData("addr", [subDomainNode]);
const result2 = await target.resolve(encodedSubDomain, calldata, {
const result = await target.resolve(encodedSubDomain, calldata, {
enableCcipRead: true,
});
const decoded = i.decodeFunctionResult("addr", result2);
const decoded = i.decodeFunctionResult("addr", result);
expect(ethers.getAddress(decoded[0])).to.equal(
ethers.getAddress(REGISTRANT_ADDR)
);
Expand All @@ -524,7 +540,6 @@ describe("Crosschain Resolver Local", () => {
});
const wrongL2BlockNumber = currentL2BlockNumberFinalized + BigInt(10);

// Construct the new data string
const resultDataModified = changeBlockNumberInCCIPResponse(
resultData,
wrongL2BlockNumber
Expand All @@ -541,6 +556,106 @@ describe("Crosschain Resolver Local", () => {
}
});

it("should revert when block number has been altered and does not match the state root hash used by the gateway", async () => {
const previousL2BlockNumber = await rollup.currentL2BlockNumber({
blockTag: "finalized",
});
await waitForLatestL2BlockNumberFinalizedToChange(
rollup,
2000,
"finalized"
);
const currentL2BlockNumber = await rollup.currentL2BlockNumber({
blockTag: "finalized",
});

expect(currentL2BlockNumber).to.be.greaterThan(previousL2BlockNumber);

const i = new ethers.Interface(["function addr(bytes32) returns(address)"]);
const calldata = i.encodeFunctionData("addr", [subDomainNode]);
try {
await target.resolve(encodedSubDomain, calldata);
} catch (e) {
const extraData: string = getExtraData(e);
const resultData: string = await fetchCCIPGateway(e);
// Construct the new data string
const resultDataModified = changeBlockNumberInCCIPResponse(
resultData,
previousL2BlockNumber
);

try {
await target.getStorageSlotsCallback(resultDataModified, extraData);
throw "Should have reverted";
} catch (error) {
expect(error.reason).to.equal(
"LineaProofHelper: invalid account proof"
);
}
}
});

it("should revert if shomei node is down and keep resolving after it starts back up", async () => {
await execDockerCommand("stop", "shomei-frontend");

const i = new ethers.Interface(["function addr(bytes32) returns(address)"]);
const calldata = i.encodeFunctionData("addr", [subDomainNode]);

try {
await target.resolve(encodedSubDomain, calldata, {
enableCcipRead: true,
});
throw "Should have reverted";
} catch (error) {
expect(error.shortMessage).to.equal(
`error encountered during CCIP fetch: "Internal server error: Error: connect ECONNREFUSED ::1:8889"`
);
}

await execDockerCommand("start", "shomei-frontend");
await setTimeout(5_000);
await waitForLatestL2BlockNumberFinalizedToChange(
rollup,
2000,
"finalized"
);
const result = await target.resolve(encodedSubDomain, calldata, {
enableCcipRead: true,
});
const decoded = i.decodeFunctionResult("addr", result);
expect(ethers.getAddress(decoded[0])).to.equal(
ethers.getAddress(REGISTRANT_ADDR)
);
});

it("should revert if l2 node is down and keep resolving after it starts back up", async () => {
await execDockerCommand("stop", "l2-node");

const i = new ethers.Interface(["function addr(bytes32) returns(address)"]);
const calldata = i.encodeFunctionData("addr", [subDomainNode]);

try {
await target.resolve(encodedSubDomain, calldata, {
enableCcipRead: true,
});
throw "Should have reverted";
} catch (error) {
expect(error.shortMessage).to.equal(
`error encountered during CCIP fetch: "Internal server error: Error: connect ECONNREFUSED ::1:8845"`
);
}

await execDockerCommand("start", "l2-node");
await setTimeout(5_000);
const result = await target.resolve(encodedSubDomain, calldata, {
enableCcipRead: true,
});
const decoded = i.decodeFunctionResult("addr", result);
expect(ethers.getAddress(decoded[0])).to.equal(
ethers.getAddress(REGISTRANT_ADDR)
);
});

after(async () => {
clearInterval(sendTransactionsPromise);
});
Expand Down
40 changes: 31 additions & 9 deletions packages/linea-ens-resolver/test/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { exec } from "child_process";
import {
BigNumberish,
Contract,
Expand Down Expand Up @@ -44,9 +45,10 @@ export async function waitForL2BlockNumberFinalized(
) {
let currentL2BlockNumberFinalized;
do {
currentL2BlockNumberFinalized = await rollup.currentL2BlockNumber({
blockTag: "finalized",
});
currentL2BlockNumberFinalized =
(await rollup.currentL2BlockNumber({
blockTag: "finalized",
})) || BigInt(0);
await setTimeout(pollingInterval);
} while (currentL2BlockNumberFinalized < afterBlockNo);
}
Expand All @@ -56,14 +58,16 @@ export async function waitForLatestL2BlockNumberFinalizedToChange(
pollingInterval: number,
blockTag: BlockTag = "latest"
) {
const currentL2BlockNumber = await rollup.currentL2BlockNumber({
blockTag,
});
const currentL2BlockNumber =
(await rollup.currentL2BlockNumber({
blockTag,
})) || BigInt(0);
let newL2BlockNumber;
do {
newL2BlockNumber = await rollup.currentL2BlockNumber({
blockTag,
});
newL2BlockNumber =
(await rollup.currentL2BlockNumber({
blockTag,
})) || BigInt(0);
await setTimeout(pollingInterval);
} while (currentL2BlockNumber >= newL2BlockNumber);
}
Expand Down Expand Up @@ -107,3 +111,21 @@ export function changeBlockNumberInCCIPResponse(

return prefix + blockNoTestHex + suffix;
}

export async function execDockerCommand(
command: string,
containerName: string
): Promise<string> {
const dockerCommand = `docker ${command} ${containerName}`;
console.log(`Executing: ${dockerCommand}...`);
return new Promise((resolve, reject) => {
exec(dockerCommand, (error, stdout, stderr) => {
if (error) {
console.error(`Error executing (${dockerCommand}): ${stderr}`);
reject(error);
}
console.log(`Execution success (${dockerCommand}): ${stdout}`);
resolve(stdout);
});
});
}

0 comments on commit 164a1b6

Please sign in to comment.