Emerge Yul recompiler (#1)

Provide a modified (and incomplete) version of ZKSync zksolc that can compile the most basic contracts
This commit is contained in:
Cyrill Leutwiler
2024-03-12 12:06:02 +01:00
committed by GitHub
parent d238d8f39e
commit cffa14a4d2
247 changed files with 35357 additions and 4905 deletions
@@ -0,0 +1,4 @@
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,24 @@
{
"name": "cli-tests",
"version": "1.0.0",
"title": "zksolc CLI Tests",
"description": "Auto tests for verifying zksolc CLI",
"repository": "https://github.com/matter-labs/era_compiler-solidity",
"main": "index.js",
"private": true,
"scripts": {
"test": "npx jest --verbose --testPathPattern="
},
"keywords": [],
"author": "Matter Labs",
"license": "MIT",
"devDependencies": {
"@types/jest": "^29.5.11",
"@types/shelljs": "^0.8.15",
"jest": "^29.7.0",
"shelljs": "^0.8.5",
"ts-jest": "^29.1.1",
"typescript": "^5.3.3"
}
}
@@ -0,0 +1,3 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
contract C {}
@@ -0,0 +1,54 @@
object "Test" {
code {
{
mstore(64, 128)
if callvalue() { revert(0, 0) }
let _1 := datasize("Test_deployed")
codecopy(0, dataoffset("Test_deployed"), _1)
return(0, _1)
}
}
object "Test_deployed" {
code {
{
mstore(64, 128)
if iszero(lt(calldatasize(), 4))
{
let _1 := 0
switch shr(224, calldataload(_1))
case 0x3df4ddf4 {
if callvalue() { revert(_1, _1) }
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
let memPos := allocate_memory(_1)
mstore(memPos, 0x2a)
return(memPos, 32)
}
case 0x5a8ac02d {
if callvalue() { revert(_1, _1) }
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
let memPos_1 := allocate_memory(_1)
return(memPos_1, sub(abi_encode_uint256(memPos_1, 0x63), memPos_1))
}
}
revert(0, 0)
}
function abi_encode_uint256(headStart, value0) -> tail
{
tail := add(headStart, 32)
mstore(headStart, value0)
}
function allocate_memory(size) -> memPtr
{
memPtr := mload(64)
let newFreePtr := add(memPtr, and(add(size, 31), not(31)))
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr))
{
mstore(0, shl(224, 0x4e487b71))
mstore(4, 0x41)
revert(0, 0x24)
}
mstore(64, newFreePtr)
}
}
}
}
@@ -0,0 +1,25 @@
.text
.file "main"
.globl __entry
__entry:
.func_begin0:
sub.s! 0, r2, r1
jump.eq @.BB0_2
add 32, r0, r1
st.1 r0, r1
st.1 r1, r0
add @CPI0_1[0], r0, r1
ret.ok.to_label r1, @DEFAULT_FAR_RETURN
.BB0_2:
add 42, r0, r1
st.1 r0, r1
add @CPI0_0[0], r0, r1
ret.ok.to_label r1, @DEFAULT_FAR_RETURN
.func_end0:
.note.GNU-stack
.rodata
CPI0_0:
.cell 2535301200456458802993406410752
CPI0_1:
.cell 5070602400912917605986812821504
@@ -0,0 +1,31 @@
import * as path from 'path';
const outputDir = 'artifacts';
const binExtension = ':C.zbin';
const asmExtension = ':C.zasm';
const contractSolFilename = 'contract.sol';
const contractYulFilename = 'contract.yul';
const contractZkasmFilename = 'contract.zkasm';
const pathToOutputDir = path.join( __dirname, '..', outputDir);
const pathToContracts = path.join( __dirname, '..', 'src', 'contracts');
const pathToBasicYulContract = path.join(pathToContracts, 'yul', contractYulFilename);
const pathToBasicZkasmContract = path.join(pathToContracts, 'zkasm', contractZkasmFilename);
const pathToBasicSolContract = path.join(pathToContracts, 'solidity', contractSolFilename);
const pathToSolBinOutputFile = path.join(pathToOutputDir, contractSolFilename + binExtension);
const pathToSolAsmOutputFile = path.join(pathToOutputDir, contractSolFilename + asmExtension);
export const paths = {
outputDir: outputDir,
binExtension: binExtension,
asmExtension: asmExtension,
contractSolFilename: contractSolFilename,
contractZkasmFilename: contractZkasmFilename,
contractYulFilename: contractYulFilename,
pathToOutputDir: pathToOutputDir,
pathToContracts: pathToContracts,
pathToBasicZkasmContract: pathToBasicZkasmContract,
pathToBasicSolContract: pathToBasicSolContract,
pathToBasicYulContract: pathToBasicYulContract,
pathToSolBinOutputFile: pathToSolBinOutputFile,
pathToSolAsmOutputFile: pathToSolAsmOutputFile,
};
@@ -0,0 +1,29 @@
import * as shell from 'shelljs';
import * as fs from 'fs';
interface CommandResult {
output: string;
exitCode: number;
}
export const executeCommand = (command: string): CommandResult => {
const result = shell.exec(command, {async: false});
return {
exitCode: result.code,
output: result.stdout.trim() || result.stderr.trim(),
};
};
export const isFolderExist = (folder: string): boolean => {
return shell.test('-d', folder);
};
export const isFileExist = (pathToFileDir: string, fileName: string, fileExtension:string): boolean => {
return shell.ls(pathToFileDir).stdout.includes(fileName + fileExtension);
};
export const isFileEmpty = (file: string): boolean => {
if (fs.existsSync(file)) {
return (fs.readFileSync(file).length === 0);
}
};
@@ -0,0 +1,39 @@
import {executeCommand} from "../src/helper";
import { paths } from '../src/entities';
//id1746
describe("Run with --asm by default", () => {
const command = `zksolc ${paths.pathToBasicSolContract} --asm`;
const result = executeCommand(command);
const commandInvalid = 'zksolc --asm';
const resultInvalid = executeCommand(commandInvalid);
it("Valid command exit code = 0", () => {
expect(result.exitCode).toBe(0);
});
it("--asm output is presented", () => {
expect(result.output).toMatch(/(__entry:)/i);
});
it("solc exit code == zksolc exit code", () => {
const command = `solc ${paths.pathToBasicSolContract} --asm`;
const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(result.exitCode);
});
it("run invalid: zksolc --asm", () => {
expect(resultInvalid.output).toMatch(/(No input sources specified|Compilation aborted)/i);
});
it("Invalid command exit code = 1", () => {
expect(resultInvalid.exitCode).toBe(1);
});
it("Invalid solc exit code == Invalid zksolc exit code", () => {
const command = 'solc --asm';
const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(resultInvalid.exitCode);
});
});
@@ -0,0 +1,78 @@
import {executeCommand, isFolderExist, isFileExist, isFileEmpty} from "../src/helper";
import { paths } from '../src/entities';
//id1762
describe("Run zksolc without any options", () => {
const command = 'zksolc';
const result = executeCommand(command);
it("Info with help is presented", () => {
expect(result.output).toMatch(/(No input sources specified|Error(s) found.)/i);
});
it("Exit code = 1", () => {
expect(result.exitCode).toBe(1);
});
it("solc exit code == zksolc exit code", () => {
const command = 'solc';
const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
//#1713
describe("Default run a command from the help", () => {
const command = `zksolc ${paths.pathToBasicSolContract} -O3 --bin --output-dir "${paths.pathToOutputDir}"`; // potential issue on zksolc with full path on Windows cmd
const result = executeCommand(command);
it("Compiler run successful", () => {
expect(result.output).toMatch(/(Compiler run successful.)/i);
});
it("Exit code = 0", () => {
expect(result.exitCode).toBe(0);
});
it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
});
xit("Output file is created", () => { // a bug on windows
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.binExtension)).toBe(true);
});
it("the output file is not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
});
it("No 'Error'/'Warning'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i);
});
});
//#1818
describe("Default run a command from the help", () => {
const command = `zksolc ${paths.pathToBasicSolContract} -O3 --bin --asm --output-dir "${paths.pathToOutputDir}"`; // potential issue on zksolc with full path on Windows cmd
const result = executeCommand(command);
it("Compiler run successful", () => {
expect(result.output).toMatch(/(Compiler run successful.)/i);
});
it("Exit code = 0", () => {
expect(result.exitCode).toBe(0);
});
it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
});
xit("Output files are created", () => { // a bug on windows
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.binExtension)).toBe(true);
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.asmExtension)).toBe(true);
});
it("the output files are not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
});
it("No 'Error'/'Warning'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i);
});
});
@@ -0,0 +1,40 @@
import {executeCommand} from "../src/helper";
import { paths } from '../src/entities';
//id1743
describe("Run with --yul by default", () => {
const command = `zksolc ${paths.pathToBasicYulContract} --yul`;
const result = executeCommand(command);
const commandInvalid = 'zksolc --yul';
const resultInvalid = executeCommand(commandInvalid);
it("Valid command exit code = 0", () => {
expect(result.exitCode).toBe(0);
});
it("--yul output is presented", () => {
expect(result.output).toMatch(/(Compiler run successful)/i);
expect(result.output).toMatch(/(No output requested)/i);
});
xit("solc exit code == zksolc exit code", () => { // unknown solc issue for datatype of the contract
const command = `solc ${paths.pathToBasicSolContract} --yul`;
const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(result.exitCode);
});
it("run invalid: zksolc --yul", () => {
expect(resultInvalid.output).toMatch(/(The input file is missing)/i);
});
it("Invalid command exit code = 1", () => {
expect(resultInvalid.exitCode).toBe(1);
});
it("Invalid solc exit code == Invalid zksolc exit code", () => {
const command = 'solc --yul';
const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(resultInvalid.exitCode);
});
});
@@ -0,0 +1,18 @@
import {executeCommand} from "../src/helper";
import { paths } from '../src/entities';
//id1745
describe("Run with --zkasm by default", () => {
const command = `zksolc ${paths.pathToBasicZkasmContract} --zkasm`;
const result = executeCommand(command);
it("Valid command exit code = 0", () => {
expect(result.exitCode).toBe(0);
});
it("--zkasm output is presented", () => {
expect(result.output).toMatch(/(Compiler run successful)/i);
expect(result.output).toMatch(/(No output requested)/i);
});
});
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "ES6",
"module": "CommonJS",
"outDir": "./dist",
"rootDir": "./src"
}
}