-
Notifications
You must be signed in to change notification settings - Fork 47
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
Predictor #440
base: development
Are you sure you want to change the base?
Predictor #440
Changes from all commits
068ce8a
15bd84f
4ec8aab
81a24c6
0990025
ed30491
bdc2d6a
25b6aa8
c51e68a
3b13453
1761842
77ac340
602e919
31d158a
a07d726
007e7fa
8deade1
1f1daeb
9d7a130
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,59 @@ | ||||||
# WHAT IS THIS SCRIPT SET FOR? | ||||||
|
||||||
This set of scripts is intended to help devs to find out if the contracts of a given commit / branch in a repo will reproduce exactly the contracts deployed in the blockchain. | ||||||
These scripts are categorized into four .js files for convenient separate execution. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pls add a high level schematic description of how it works as a sequence of steps like
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. btw can it be extended with automatic verifier? :) |
||||||
This can be used as a tool to predict if the contract code in certain repo / branch / commit will verify in a block explorer. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
## Scripts and JSON Files | ||||||
|
||||||
The folder ./scripts/predictor have four .js files and may have several .json files, but one them is invariant: contract_config.json | ||||||
|
||||||
These scripts will work only provided that there exist a folder in the repo named ./scripts/contractInteraction contentive of the .json files listing all the Sovryn's deployed contracts. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
pls consider putting all settings - paths etc. in e.g. .deployment-verifier.json or .ts/.js |
||||||
|
||||||
The script files are: | ||||||
|
||||||
- createJSON.js --> to initialize the dump .json files with the expected format | ||||||
- findFiles.js --> to complete information in the files created by the former script | ||||||
- fillJSON.js --> to help to fill dump files when data is provided by hand | ||||||
- comparator.js --> generate a report based on the info placed in dump files, and predict if the contracts held in the repo will generate a bytecode congruent with the deployed bytecode. | ||||||
|
||||||
This scripts are intended to be universal and independent on how old the solidity compiler was the chosen to compile and deploy. | ||||||
|
||||||
The contract_config.json file will content precise information about the .json files that must be present in the folder ./scripts/contractInteraction, about the network providers and the dump files. Anyone can extend the initial content of the file, but the info must be right or the scripts will fail. | ||||||
|
||||||
## How to Use These Scripts | ||||||
|
||||||
1. Clone locally the smart contracts git repo. | ||||||
2. Install the dependencies as instructed in the RAEADME.md file of that repo. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
3. Execute the compiling script. | ||||||
This will generate the needed files and folders with all the info on compilation needed for this script. More info about this can be found [here](https://wiki.sovryn.app/en/technical-documents/API/ApiDoc#h-3-compiling-contracts) for amm repo, and [here](https://wiki.sovryn.app/en/technical-documents/API/ApiDoc#h-3-compile-all-the-contracts-with-hard-hat) for the Sovryn protocol. | ||||||
4. `./scripts/predictor/contract_config.json` can be edited to extend it. Be ware that the info must be precise or the script may fail with unexpected errors. | ||||||
5. Check the info in folder `./scripts/contractInteraction` and make sure that at least that the files `mainnet_contracts.json` and `testnet_contracts.json` are present with the relevant info. These files are expected to contain as accurate and complete information as possible, about contract deployments on chain. | ||||||
6. Go to the folder `./scripts/predictor` and execute: | ||||||
Comment on lines
+30
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pls consider adding paths to a config like .deployment-verifier.json |
||||||
|
||||||
``` | ||||||
$ node createJSON | ||||||
``` | ||||||
|
||||||
After this, files like `m_deployed_compiled.json` mut be present. Be ware that if there was a previous .json file with that name, this script will overwrite it. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pls consider adding it as parameter to cover cases when you can add an external ABI manually and those won't be overwritten There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
7. Now execute: | ||||||
|
||||||
``` | ||||||
$ node findFiles | ||||||
``` | ||||||
This will fill the dump files with the paths of the files with the conpiled bytecodes. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
8. We can now inspect the modified dump files. It is not expected that all the field have been filled. If there are still missing information we can copy/paste these dump files in other smart contract local repos in which we have performed the compilation scripts. Then we can repeat the step N° 7 in that other repository, and the dump files will be filled with additional information that can only be located in that other repo. | ||||||
9. We can inspect the modified dump files. If there is still missing information that can be found in the repositories, we can fill such data by hand. We can use the script `fillJSON.js` to automatically copy/paste the new info from the file of one network to its couple. E.g.: if we filled by hand data in `m_deployed_compiled.json`, we can use `fillJSON.js` to copy such data to `t_deployed_compiled.json`. The way to do this is by executing: | ||||||
|
||||||
``` | ||||||
$ node fillJSON <file_path_FROM_file> <file_path_TO_file> | ||||||
``` | ||||||
|
||||||
10. Once we have dump files with the enough information or the expected amount of data, we can now execute: | ||||||
|
||||||
``` | ||||||
$ node comparator | ||||||
``` | ||||||
This script will generate a report in the file: `scripts/predictor/report` which will be re-written if present. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"multisig": ["0xCc653b64e8f4f2aDEa87490f11d090472E08838A", "./"] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,291 @@ | ||
const { ethers } = require("ethers"); | ||
const fs = require("fs"); | ||
const revision = require("child_process"); | ||
var commHash = revision.execSync("git rev-parse HEAD"); | ||
commHash = commHash.toString().substring(0, commHash.length - 1); | ||
var origin = revision.execSync("git config --get remote.origin.url"); | ||
origin = origin.toString().substring(0, origin.length - 1); | ||
var branch = revision.execSync("git branch"); | ||
branch = branch | ||
.toString() | ||
.substring(0, branch.length - 1) | ||
.slice(2); | ||
|
||
const FIX_ID = 32 * 2; | ||
|
||
console.log("------------------comparator script-----------------------", "\n", "\n", "\n"); | ||
|
||
const configContract = "./contract_config.json"; | ||
|
||
// this is the main function; it takes the config JSON to guide the runs | ||
// this config is still in the form of the path of a file | ||
async function iterator(CfgC) { | ||
// we take the JSON object, from the path og the file | ||
// it is assumed that CfgC exists | ||
var F = require(CfgC); | ||
// we measure the length of the JSON object | ||
var L = Object.keys(F).length; | ||
|
||
// we start a stream of data to feed the report file | ||
var STR = initializeReport("./report"); | ||
|
||
// the headers for both the report and the console | ||
STR.write( | ||
"Prediction Report: to know if the code on this repo will produce bytecode that will verify in a block explorer" + | ||
"\n" + | ||
"\n" + | ||
"\n" | ||
); | ||
STR.write( | ||
"Repository info: " + | ||
"\n" + | ||
"URL: " + | ||
origin + | ||
"\n" + | ||
"Branch: " + | ||
branch + | ||
"\n" + | ||
"Commit: " + | ||
commHash + | ||
"\n" + | ||
"\n" + | ||
"\n" | ||
); | ||
console.log( | ||
"Prediction Report: to know if the code on this repo will produce bytecode that will verify in a block explorer", | ||
"\n", | ||
"\n", | ||
"\n" | ||
); | ||
console.log( | ||
"Repository info: ", | ||
"\n", | ||
"URL: ", | ||
origin, | ||
"\n", | ||
"Branch: ", | ||
branch, | ||
"\n", | ||
"Commit: ", | ||
commHash, | ||
"\n", | ||
"\n", | ||
"\n" | ||
); | ||
|
||
// the loop to cycle through all the lists of contracts for all the networks described in CfgC | ||
for (let i = 0; i < L; ++i) { | ||
let ki = Object.keys(F)[i]; // this is the network id | ||
STR.write("\n contracts dpeloyed in the network N°: " + ki + "\n" + "\n"); | ||
console.log("\n contracts dpeloyed in the network N°: ", ki + "\n" + "\n"); | ||
let vi = F[ki][2]; // this is the path to the JSON file with the list of contracts for that network id | ||
let Provi = F[ki][1]; // this is Sovryn's provider endpoint for that network id | ||
STR.write("\n using provider: " + Provi + "\n" + "\n"); | ||
console.log("\n using provider: ", Provi, "\n", "\n"); | ||
let pi = new ethers.providers.JsonRpcProvider(Provi); // ethers provider | ||
|
||
// we assume that the string vi is rightfully written | ||
if (fs.existsSync(vi)) { | ||
let C = require(vi); // this is the JSON object with the data of contracts | ||
let Lc = Object.keys(C).length; // this is how many contract are listed in that JSON | ||
STR.write("checking " + Lc + " contracts." + "\n" + "\n"); | ||
console.log("checking ", Lc, " contracts.", "\n", "\n"); | ||
|
||
// the loop to cycle through all the contracts of a network's list | ||
for (let j = 0; j < Lc; ++j) { | ||
kj = Object.keys(C)[j]; // this is the name of the contract | ||
STR.write( | ||
"checking contract N°: " + | ||
(j + 1) + | ||
" from " + | ||
Lc + | ||
", named: " + | ||
kj + | ||
"\n" | ||
); | ||
console.log( | ||
"checking contract N°: ", | ||
j + 1, | ||
" from ", | ||
Lc, | ||
", named: ", | ||
kj, | ||
"\n" | ||
); | ||
let Ad = C[kj][0]; // this is the address of the deployment of that contract in that network id | ||
Ad = Ad.toLowerCase(); | ||
let Pt = C[kj][1]; // this is the path of the JSON file produced by the compilation, with the predicted bytecode | ||
|
||
// we need assure that Pt is rightfully written | ||
if (Pt != null && Pt != undefined && Pt != "./") { | ||
// we need make sure that the file in the path Pt exists | ||
if (fs.existsSync(Pt)) { | ||
let Bytc = require(Pt); // this is the JSON object holding the compilation's bytecode | ||
let B0 = Bytc["deployedBytecode"]; // This is the compilation's bytecode | ||
// preventing missing URL response error | ||
let B1 = ethers.utils.isAddress(Ad) ? await getCode(pi, Ad, 1) : "0x"; // This is the bytecode from the deployed contract in blockchain | ||
let veredict = | ||
(B0 != undefined && B1 != undefined && B0 != null && B1 != null) | ||
? compare(B0, B1) | ||
: false; // true: it should verify; false: it won't verify | ||
if (veredict) { | ||
let success = | ||
"the contract " + | ||
kj + | ||
", deployed in the network N° " + | ||
ki + | ||
", with the address " + | ||
Ad + | ||
" will successfully verify \n"; | ||
STR.write(success); | ||
console.log(success); | ||
} else { | ||
let fail = | ||
"the contract " + | ||
kj + | ||
", deployed in the network N° " + | ||
ki + | ||
", with the address " + | ||
Ad + | ||
" will NOT verify \n"; | ||
STR.write(fail); | ||
console.log(fail); | ||
} | ||
} else { | ||
STR.write( | ||
"the bytecode for the contract " + kj + " may not be in this repo" + "\n" | ||
); | ||
console.log( | ||
"the bytecode for the contract ", | ||
kj, | ||
" may not be in this repo", | ||
"\n" | ||
); | ||
} | ||
} else { | ||
STR.write( | ||
"the compilation for the contract " + kj + " has not been done yet" + "\n" | ||
); | ||
console.log( | ||
"the compilation for the contract ", | ||
kj, | ||
" has not been done yet", | ||
"\n" | ||
); | ||
} | ||
} | ||
} else { | ||
STR.write( | ||
"make sure createJSON was run first, " + | ||
vi + | ||
" file not initialized" + | ||
"\n" | ||
); | ||
console.log( | ||
"make sure createJSON was run first, ", | ||
vi, | ||
" file not initialized", | ||
"\n" | ||
); | ||
} | ||
} | ||
// bug: iterator do not verify if a given file exist or not in a path | ||
STR.end(); | ||
} | ||
|
||
// preventing missing URL response error | ||
async function getCode(provider, Addrss, tries) { | ||
console.log('\n number of tries: ', tries, '\n'); | ||
let byCd = '0x'; | ||
try { | ||
byCd = await provider.getCode(Addrss); | ||
return byCd; | ||
} catch (error) { | ||
++tries; | ||
console.log('\n we got this error: ', error, '\n'); | ||
if (tries < 11) { | ||
byCd = await getCode(provider, Addrss, tries); | ||
return byCd; | ||
} | ||
} | ||
if (tries = 11 && | ||
byCd == '0x') { | ||
console.log('\n DO NOT TRUST NEXT VEREDICT \n'); | ||
return byCd; | ||
} | ||
} | ||
|
||
// this function guides the generation of stream of data for the report | ||
function initializeReport(report_path) { | ||
// writtable streams with fs: | ||
// https://stackoverflow.com/questions/3459476/how-to-append-to-a-file-in-node/43370201#43370201 | ||
// according to: https://nodejs.org/api/fs.html#file-system-flags | ||
// File system flag 'a': Open file for appending. The file is created if it does not exist. | ||
var stream = fs.createWriteStream(report_path, { flags: "a" }); | ||
return stream; | ||
} | ||
|
||
function compare(A, B) { | ||
|
||
var bytes = select(A, B); | ||
// fixing TypeError | ||
A = bytes[0]; | ||
B = bytes[1]; | ||
bytes = (A != B ) ? reduce(A, B) : [A, B]; | ||
|
||
x = bytes[0] == bytes[1]; | ||
|
||
// console.log(bytes[0], bytes[1]); | ||
return x; | ||
|
||
} | ||
|
||
function sizes(A, B) { | ||
return A.length == B.length && isPair(A.length); | ||
} | ||
|
||
function select(A, B) { | ||
if (!sizes(A, B)) { | ||
console.log("bytecode sizes not equal or invalid"); | ||
return ["wrong", "arguments"]; | ||
} | ||
// fixing returning an undefined object | ||
let flag = false; | ||
for (let i = A.length; i > 2; i--) { | ||
|
||
if (A[i - 1] != B[i - 1]) { | ||
flag = true; | ||
if (isPair(i)) { | ||
A = A.slice(0, i); | ||
B = B.slice(0, i); | ||
} else { | ||
A = A.slice(0, i + 1); | ||
B = B.slice(0, i + 1); | ||
} | ||
|
||
return [A, B]; | ||
} | ||
|
||
} | ||
if (!flag) return [A, B]; | ||
} | ||
|
||
function reduce(A, B) { | ||
if (!sizes(A, B) || A.length <= FIX_ID + 2) { | ||
console.log("bytecode sizes not equal, invalid or too short"); | ||
return ["wrong", "arguments"]; | ||
} | ||
|
||
x = A.length - FIX_ID; | ||
|
||
A = A.slice(0, x); | ||
B = B.slice(0, x); | ||
|
||
return [A, B]; | ||
} | ||
|
||
function isPair(X) { | ||
return X % 2 == 0; | ||
} | ||
|
||
iterator(configContract); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"30": [ | ||
"../contractInteraction/mainnet_contracts.json", | ||
"https://mainnet.sovryn.app/rpc", | ||
"./m_deployed_compiled.json" | ||
], | ||
"31": [ | ||
"../contractInteraction/testnet_contracts.json", | ||
"https://testnet.sovryn.app/rpc", | ||
"./t_deployed_compiled.json" | ||
], | ||
"97": [ | ||
"../contractInteraction/bsc_testnet_contracts.json", | ||
"https://bsc.sovryn.app/testnet", | ||
"./bscT_deployed_compiled.json" | ||
] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This set of scripts is intended to help devs to find out if the contracts ABI of a given commit / branch in a repo correspond to the contracts deployed in the blockchain.