-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
894a77c
commit 65c2be9
Showing
6 changed files
with
263 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
const fs = require("fs"); | ||
const path = require("path"); | ||
const { execSync } = require("child_process"); | ||
|
||
function getFileNames(directory) { | ||
return fs.readdirSync(directory).map((file) => { | ||
const baseName = path.basename(file, ".sol"); | ||
return path.parse(baseName).name; | ||
}); | ||
} | ||
|
||
let fileNames = []; | ||
fileNames = fileNames.concat(getFileNames("src/diamonds/nayms/facets/")); | ||
fileNames = fileNames.concat(getFileNames("src/diamonds/shared/facets/")); | ||
|
||
const namesToRemove = ["DiamondCutFacet", "OwnershipFacet"]; | ||
|
||
fileNames = fileNames.filter((name) => !namesToRemove.includes(name)); | ||
|
||
fileNames.push("Nayms"); | ||
|
||
fileNames.forEach((name, index) => { | ||
console.log(`contractName[${index}] = "${name}";`); | ||
}); | ||
|
||
const formattedFileNames = fileNames.map((name) => `'${name}'`); | ||
|
||
const arrayString = `[${formattedFileNames.join(",")}]`; | ||
|
||
console.log(formattedFileNames); | ||
|
||
let cmd = `forge script CodeRecon -s "run(string[] memory)" ${arrayString} -f ${process.env.ETH_MAINNET_RPC_URL} --chain-id 1 --etherscan-api-key ${process.env.ETHERSCAN_API_KEY} -vv`; | ||
|
||
console.log(`Running command forge script CodeRecon`); | ||
|
||
let output = execSync(cmd); | ||
console.log(`Output: ${output}`); | ||
|
||
console.log("Running parse-json.js"); | ||
execSync("node cli-tools/parse-json.js"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
const fs = require("fs"); | ||
const path = require("path"); | ||
|
||
function getFileNames(directory) { | ||
return fs.readdirSync(directory).map((file) => { | ||
const baseName = path.basename(file, ".sol"); | ||
return path.parse(baseName).name; | ||
}); | ||
} | ||
|
||
let fileNames = []; | ||
|
||
for (let i = 2; i < process.argv.length; i++) { | ||
const directoryPath = process.argv[i]; | ||
fileNames = fileNames.concat(getFileNames(directoryPath)); | ||
} | ||
|
||
const namesToRemove = ["DiamondCutFacet", "OwnershipFacet"]; | ||
|
||
fileNames = fileNames.filter((name) => !namesToRemove.includes(name)); | ||
|
||
fileNames.push("Nayms"); | ||
|
||
fileNames.forEach((name, index) => { | ||
console.log(`contractName[${index}] = "${name}";`); | ||
}); | ||
|
||
const formattedFileNames = fileNames.map((name) => `"${name}"`); | ||
|
||
const arrayString = `'[${formattedFileNames.join(",")}]'`; | ||
|
||
console.log(arrayString); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
const fs = require("fs"); | ||
|
||
let data = fs.readFileSync("codeReconReport.json", "utf8"); | ||
data = JSON.parse(data); | ||
|
||
let cleanedString = data.reconResult | ||
.replace(/\\"/g, '"') | ||
.replace(/"\{/g, "{") | ||
.replace(/\}"/g, "}") | ||
.slice(1, -1); | ||
cleanedString = "[" + cleanedString + "]"; | ||
let cleanedJSON = JSON.parse(cleanedString); | ||
|
||
data.reconResult = cleanedJSON; | ||
|
||
fs.writeFileSync("codeReconReport.json", JSON.stringify(data, null, 2)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.17; | ||
|
||
import { strings } from "lib/solidity-stringutils/src/strings.sol"; | ||
import { IDiamondLoupe } from "src/diamonds/shared/interfaces/IDiamondLoupe.sol"; | ||
import "script/utils/DeploymentHelpers.sol"; | ||
|
||
struct ReconResult { | ||
string contractName; | ||
address contractAddress; | ||
bytes artifactCode; | ||
bytes onChainCode; | ||
bool isMatchingWithMetadata; | ||
bool isMatchingWithoutMetadata; | ||
} | ||
struct ReconInfo { | ||
uint256 chainId; | ||
uint256 blockNumber; | ||
uint256 timestamp; | ||
ReconResult[] reconResult; | ||
address[] addressesNotMatching; | ||
string[] namesNotMatching; | ||
} | ||
|
||
library LibSearch { | ||
function arrayIntersection( | ||
mapping(string => bool) storage matchResultMapping, | ||
mapping(bytes32 => bool) storage tMap, | ||
mapping(bytes32 => string) storage codeToName, | ||
bytes32[] memory arr1, | ||
bytes32[] memory arr2 | ||
) internal { | ||
// Loop through the second array and map all elements to true | ||
for (uint256 i; i < arr2.length; i++) { | ||
tMap[arr2[i]] = true; | ||
} | ||
|
||
// Loop through the first array and check if any element exists in the map | ||
for (uint256 i; i < arr1.length; i++) { | ||
if (tMap[arr1[i]]) { | ||
matchResultMapping[codeToName[arr1[i]]] = true; | ||
} | ||
} | ||
} | ||
} | ||
|
||
contract CodeRecon is DeploymentHelpers { | ||
using strings for *; | ||
using LibSearch for mapping(string => bool); | ||
|
||
mapping(bytes32 => bool) tMap; | ||
address[] public contractAddresses; | ||
string[] public contractNames; | ||
bytes32[] public onChainCode; | ||
bytes32[] public onChainCodeWithoutMetadata; | ||
bytes32[] public artifactCode; | ||
bytes32[] public artifactCodeWithoutMetadata; | ||
mapping(bytes32 => string) public artifactCodeToName; | ||
mapping(bytes32 => string) public artifactCodeWithoutMetadataToName; | ||
mapping(string => bool) public matching; | ||
mapping(string => bool) public matchingWithoutMetadata; | ||
address[] addressesNotMatching; | ||
string[] namesNotMatching; | ||
|
||
mapping(bytes32 => address) onChainCodeToAddress; | ||
mapping(address => bytes32) addressToOnChainCode; | ||
mapping(bytes32 => address) onChainCodeWithoutMetadataToAddress; | ||
mapping(address => bytes32) addressToOnChainCodeWithoutMetadata; | ||
|
||
function getOnchainCode(address proxyDiamondAddress) public { | ||
strings.slice memory needle1 = "a264".toSlice(); | ||
|
||
IDiamondLoupe diamond = IDiamondLoupe(proxyDiamondAddress); | ||
contractAddresses = diamond.facetAddresses(); | ||
contractAddresses.push(proxyDiamondAddress); | ||
|
||
for (uint256 i; i < contractAddresses.length; ++i) { | ||
onChainCode.push(keccak256(contractAddresses[i].code)); | ||
onChainCodeWithoutMetadata.push(keccak256(bytes(vm.toString(contractAddresses[i].code).toSlice().rfind(needle1).toString()))); | ||
|
||
onChainCodeToAddress[keccak256(contractAddresses[i].code)] = contractAddresses[i]; | ||
addressToOnChainCode[contractAddresses[i]] = keccak256(contractAddresses[i].code); | ||
onChainCodeWithoutMetadataToAddress[keccak256(bytes(vm.toString(contractAddresses[i].code).toSlice().rfind(needle1).toString()))] = contractAddresses[i]; | ||
addressToOnChainCodeWithoutMetadata[contractAddresses[i]] = keccak256(bytes(vm.toString(contractAddresses[i].code).toSlice().rfind(needle1).toString())); | ||
} | ||
} | ||
|
||
function getArtifactCode(string[] memory contractName) public { | ||
contractNames = contractName; | ||
|
||
strings.slice memory needle1 = "a264".toSlice(); | ||
for (uint256 i; i < contractName.length; ++i) { | ||
bytes memory artifactCode_ = vm.getDeployedCode(string.concat(contractName[i], ".sol:", contractName[i])); | ||
bytes32 keccakArtifactCode = keccak256(artifactCode_); | ||
artifactCode.push(keccak256(artifactCode_)); | ||
artifactCodeToName[keccakArtifactCode] = contractName[i]; | ||
|
||
bytes32 artifactCodeWithoutMetadata_ = keccak256(bytes(vm.toString(artifactCode_).toSlice().rfind(needle1).toString())); | ||
artifactCodeWithoutMetadata.push(artifactCodeWithoutMetadata_); | ||
artifactCodeWithoutMetadataToName[artifactCodeWithoutMetadata_] = contractName[i]; | ||
} | ||
} | ||
|
||
function compareCode(string[] memory contractName) public { | ||
getArtifactCode(contractName); | ||
getOnchainCode(getDiamondAddressFromFile()); | ||
|
||
matching.arrayIntersection(tMap, artifactCodeToName, onChainCode, artifactCode); | ||
matchingWithoutMetadata.arrayIntersection(tMap, artifactCodeWithoutMetadataToName, onChainCodeWithoutMetadata, artifactCodeWithoutMetadata); | ||
for (uint256 i; i < contractAddresses.length; ++i) { | ||
if (matchingWithoutMetadata[artifactCodeWithoutMetadataToName[onChainCodeWithoutMetadata[i]]]) { | ||
console2.log("Bytecode WITHOUT metadata matches for facet", artifactCodeWithoutMetadataToName[onChainCodeWithoutMetadata[i]], vm.toString(contractAddresses[i])); | ||
} | ||
} | ||
|
||
for (uint256 i; i < contractAddresses.length; ++i) { | ||
if (!matchingWithoutMetadata[artifactCodeWithoutMetadataToName[onChainCodeWithoutMetadata[i]]]) { | ||
console2.log( | ||
"Bytecode WITHOUT metadata DOES NOT MATCH for facet", | ||
artifactCodeWithoutMetadataToName[onChainCodeWithoutMetadata[i]], | ||
vm.toString(contractAddresses[i]) | ||
); | ||
addressesNotMatching.push(contractAddresses[i]); | ||
} | ||
} | ||
} | ||
|
||
function genOutput() public { | ||
string memory path = "codeReconReport.json"; | ||
|
||
vm.serializeUint("ReconInfo", "chainId", uint256(block.chainid)); | ||
vm.serializeUint("ReconInfo", "blockNumber", uint256(block.number)); | ||
vm.serializeUint("ReconInfo", "timestamp", uint256(block.timestamp)); | ||
|
||
string[] memory reconResults = new string[](contractAddresses.length); | ||
|
||
for (uint256 i; i < contractAddresses.length; ++i) { | ||
if (!matchingWithoutMetadata[contractNames[i]]) { | ||
namesNotMatching.push(contractNames[i]); | ||
} | ||
|
||
vm.serializeString(string.concat("reconResult", vm.toString(i)), "contractName", artifactCodeWithoutMetadataToName[onChainCodeWithoutMetadata[i]]); | ||
vm.serializeAddress(string.concat("reconResult", vm.toString(i)), "contractAddress", onChainCodeWithoutMetadataToAddress[onChainCodeWithoutMetadata[i]]); | ||
// vm.serializeBytes(string.concat("reconResult", vm.toString(i)), "artifactCode", reconResult.artifactCode); | ||
// vm.serializeBytes(string.concat("reconResult", vm.toString(i)), "onChainCode", reconResult.onChainCode); | ||
vm.serializeBool(string.concat("reconResult", vm.toString(i)), "isMatchingWithMetadata", matching[artifactCodeToName[onChainCode[i]]]); | ||
reconResults[i] = vm.serializeBool( | ||
string.concat("reconResult", vm.toString(i)), | ||
"isMatchingWithoutMetadata", | ||
matchingWithoutMetadata[artifactCodeWithoutMetadataToName[onChainCodeWithoutMetadata[i]]] | ||
); | ||
} | ||
|
||
vm.serializeAddress("ReconInfo", "addressesNotMatching", addressesNotMatching); | ||
vm.serializeString("ReconInfo", "namesNotMatching", namesNotMatching); | ||
|
||
string memory finalJson = vm.serializeString("ReconInfo", "reconResult", reconResults); | ||
vm.writeJson(finalJson, path); | ||
} | ||
|
||
function run(string[] memory contractName) public { | ||
compareCode(contractName); | ||
genOutput(); | ||
} | ||
} |