mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-05-08 13:38:12 +00:00
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:
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
preset: "ts-jest",
|
||||
testEnvironment: "node",
|
||||
};
|
||||
+3812
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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
//!
|
||||
//! The Solidity compiler unit tests for factory dependencies.
|
||||
//!
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
pub const MAIN_CODE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.4.16;
|
||||
|
||||
import "./callable.sol";
|
||||
|
||||
contract Main {
|
||||
function main() external returns(uint256) {
|
||||
Callable callable = new Callable();
|
||||
|
||||
callable.set(10);
|
||||
return callable.get();
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
pub const CALLABLE_CODE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.4.16;
|
||||
|
||||
contract Callable {
|
||||
uint256 value;
|
||||
|
||||
function set(uint256 x) external {
|
||||
value = x;
|
||||
}
|
||||
|
||||
function get() external view returns(uint256) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn default() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("main.sol".to_owned(), MAIN_CODE.to_owned());
|
||||
sources.insert("callable.sol".to_owned(), CALLABLE_CODE.to_owned());
|
||||
|
||||
let output = super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
SolcPipeline::Yul,
|
||||
era_compiler_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Build failure");
|
||||
|
||||
assert_eq!(
|
||||
output
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Missing field `contracts`")
|
||||
.get("main.sol")
|
||||
.expect("Missing file `main.sol`")
|
||||
.get("Main")
|
||||
.expect("Missing contract `main.sol:Main`")
|
||||
.factory_dependencies
|
||||
.as_ref()
|
||||
.expect("Missing field `factory_dependencies`")
|
||||
.len(),
|
||||
1,
|
||||
"Expected 1 factory dependency in `main.sol:Main`"
|
||||
);
|
||||
assert_eq!(
|
||||
output
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Missing field `contracts`")
|
||||
.get("callable.sol")
|
||||
.expect("Missing file `callable.sol`")
|
||||
.get("Callable")
|
||||
.expect("Missing contract `callable.sol:Callable`")
|
||||
.factory_dependencies
|
||||
.as_ref()
|
||||
.expect("Missing field `factory_dependencies`")
|
||||
.len(),
|
||||
0,
|
||||
"Expected 0 factory dependencies in `callable.sol:Callable`"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
//!
|
||||
//! The Solidity compiler unit tests for IR artifacts.
|
||||
//!
|
||||
//! The tests check if the IR artifacts are kept in the final output.
|
||||
//!
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
#[test]
|
||||
fn yul() {
|
||||
let source_code = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract Test {
|
||||
function main() public view returns (uint) {
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), source_code.to_owned());
|
||||
|
||||
let build = super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
SolcPipeline::Yul,
|
||||
era_compiler_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
|
||||
assert!(
|
||||
build
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Always exists")
|
||||
.get("test.sol")
|
||||
.expect("Always exists")
|
||||
.get("Test")
|
||||
.expect("Always exists")
|
||||
.ir_optimized
|
||||
.is_some(),
|
||||
"Yul IR is missing"
|
||||
);
|
||||
assert!(
|
||||
build
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Always exists")
|
||||
.get("test.sol")
|
||||
.expect("Always exists")
|
||||
.get("Test")
|
||||
.expect("Always exists")
|
||||
.evm
|
||||
.as_ref()
|
||||
.expect("EVM object is missing")
|
||||
.assembly
|
||||
.is_none(),
|
||||
"EVMLA IR is present although not requested"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn evmla() {
|
||||
let source_code = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract Test {
|
||||
function main() public view returns (uint) {
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), source_code.to_owned());
|
||||
|
||||
let build = super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
SolcPipeline::EVMLA,
|
||||
era_compiler_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
assert!(
|
||||
build
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Always exists")
|
||||
.get("test.sol")
|
||||
.expect("Always exists")
|
||||
.get("Test")
|
||||
.expect("Always exists")
|
||||
.evm
|
||||
.as_ref()
|
||||
.expect("EVM object is missing")
|
||||
.assembly
|
||||
.is_some(),
|
||||
"EVMLA IR is missing",
|
||||
);
|
||||
assert!(
|
||||
build
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Always exists")
|
||||
.get("test.sol")
|
||||
.expect("Always exists")
|
||||
.get("Test")
|
||||
.expect("Always exists")
|
||||
.ir_optimized
|
||||
.is_none(),
|
||||
"Yul IR is present although not requested",
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
//!
|
||||
//! The Solidity compiler unit tests for libraries.
|
||||
//!
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
pub const LIBRARY_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
// A simple library with at least one external method
|
||||
library SimpleLibrary {
|
||||
function add(uint256 a, uint256 b) external pure returns (uint256) {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
// A contract calling that library
|
||||
contract SimpleContract {
|
||||
using SimpleLibrary for uint256;
|
||||
|
||||
function performAlgorithm(uint256 a, uint256 b) public pure returns (uint256) {
|
||||
uint sum = 0;
|
||||
if (a > b) {
|
||||
while (true) {
|
||||
sum += a.add(b);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn not_specified() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), LIBRARY_TEST_SOURCE.to_owned());
|
||||
|
||||
for pipeline in [SolcPipeline::EVMLA, SolcPipeline::Yul] {
|
||||
let output = super::build_solidity_and_detect_missing_libraries(
|
||||
sources.clone(),
|
||||
BTreeMap::new(),
|
||||
pipeline,
|
||||
)
|
||||
.expect("Test failure");
|
||||
assert!(
|
||||
output
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Always exists")
|
||||
.get("test.sol")
|
||||
.expect("Always exists")
|
||||
.get("SimpleContract")
|
||||
.expect("Always exists")
|
||||
.missing_libraries
|
||||
.as_ref()
|
||||
.expect("Always exists")
|
||||
.contains("test.sol:SimpleLibrary"),
|
||||
"Missing library not detected"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn specified() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), LIBRARY_TEST_SOURCE.to_owned());
|
||||
|
||||
let mut libraries = BTreeMap::new();
|
||||
libraries
|
||||
.entry("test.sol".to_string())
|
||||
.or_insert_with(BTreeMap::new)
|
||||
.entry("SimpleLibrary".to_string())
|
||||
.or_insert("0x00000000000000000000000000000000DEADBEEF".to_string());
|
||||
|
||||
for pipeline in [SolcPipeline::EVMLA, SolcPipeline::Yul] {
|
||||
let output = super::build_solidity_and_detect_missing_libraries(
|
||||
sources.clone(),
|
||||
libraries.clone(),
|
||||
pipeline,
|
||||
)
|
||||
.expect("Test failure");
|
||||
assert!(
|
||||
output
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Always exists")
|
||||
.get("test.sol")
|
||||
.expect("Always exists")
|
||||
.get("SimpleContract")
|
||||
.expect("Always exists")
|
||||
.missing_libraries
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.is_empty(),
|
||||
"The list of missing libraries must be empty"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
//!
|
||||
//! The Solidity compiler unit tests for messages.
|
||||
//!
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::warning::Warning;
|
||||
|
||||
pub const ECRECOVER_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract ECRecoverExample {
|
||||
function recoverAddress(
|
||||
bytes32 messageHash,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) public pure returns (address) {
|
||||
return ecrecover(messageHash, v, r, s);
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn ecrecover() {
|
||||
assert!(
|
||||
super::check_solidity_warning(
|
||||
ECRECOVER_TEST_SOURCE,
|
||||
"Warning: It looks like you are using 'ecrecover' to validate a signature of a user account.",
|
||||
BTreeMap::new(),
|
||||
SolcPipeline::Yul,
|
||||
false,
|
||||
None,
|
||||
).expect("Test failure")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ecrecover_suppressed() {
|
||||
assert!(
|
||||
!super::check_solidity_warning(
|
||||
ECRECOVER_TEST_SOURCE,
|
||||
"Warning: It looks like you are using 'ecrecover' to validate a signature of a user account.",
|
||||
BTreeMap::new(),
|
||||
SolcPipeline::Yul,
|
||||
false,
|
||||
Some(vec![Warning::EcRecover]),
|
||||
).expect("Test failure")
|
||||
);
|
||||
}
|
||||
|
||||
pub const SEND_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract SendExample {
|
||||
address payable public recipient;
|
||||
|
||||
constructor(address payable _recipient) {
|
||||
recipient = _recipient;
|
||||
}
|
||||
|
||||
function forwardEther() external payable {
|
||||
bool success = recipient.send(msg.value);
|
||||
require(success, "Failed to send Ether");
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn send() {
|
||||
assert!(
|
||||
super::check_solidity_warning(
|
||||
SEND_TEST_SOURCE,
|
||||
"Warning: It looks like you are using '<address payable>.send/transfer(<X>)' without providing",
|
||||
BTreeMap::new(),
|
||||
SolcPipeline::Yul,
|
||||
false,
|
||||
None,
|
||||
).expect("Test failure")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_suppressed() {
|
||||
assert!(
|
||||
!super::check_solidity_warning(
|
||||
SEND_TEST_SOURCE,
|
||||
"Warning: It looks like you are using '<address payable>.send/transfer(<X>)' without providing",
|
||||
BTreeMap::new(),
|
||||
SolcPipeline::Yul,
|
||||
false,
|
||||
Some(vec![Warning::SendTransfer]),
|
||||
).expect("Test failure")
|
||||
);
|
||||
}
|
||||
|
||||
pub const TRANSFER_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract TransferExample {
|
||||
address payable public recipient;
|
||||
|
||||
constructor(address payable _recipient) {
|
||||
recipient = _recipient;
|
||||
}
|
||||
|
||||
function forwardEther() external payable {
|
||||
recipient.transfer(msg.value);
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn transfer() {
|
||||
assert!(
|
||||
super::check_solidity_warning(
|
||||
TRANSFER_TEST_SOURCE,
|
||||
"Warning: It looks like you are using '<address payable>.send/transfer(<X>)' without providing",
|
||||
BTreeMap::new(),
|
||||
SolcPipeline::Yul,
|
||||
false,
|
||||
None,
|
||||
).expect("Test failure")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transfer_suppressed() {
|
||||
assert!(
|
||||
!super::check_solidity_warning(
|
||||
TRANSFER_TEST_SOURCE,
|
||||
"Warning: It looks like you are using '<address payable>.send/transfer(<X>)' without providing",
|
||||
BTreeMap::new(),
|
||||
SolcPipeline::Yul,
|
||||
false,
|
||||
Some(vec![Warning::SendTransfer]),
|
||||
).expect("Test failure")
|
||||
);
|
||||
}
|
||||
|
||||
pub const EXTCODESIZE_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract ExternalCodeSize {
|
||||
function getExternalCodeSize(address target) public view returns (uint256) {
|
||||
uint256 codeSize;
|
||||
assembly {
|
||||
codeSize := extcodesize(target)
|
||||
}
|
||||
return codeSize;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn extcodesize() {
|
||||
assert!(super::check_solidity_warning(
|
||||
EXTCODESIZE_TEST_SOURCE,
|
||||
"Warning: Your code or one of its dependencies uses the 'extcodesize' instruction,",
|
||||
BTreeMap::new(),
|
||||
SolcPipeline::Yul,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extcodesize_suppressed() {
|
||||
assert!(!super::check_solidity_warning(
|
||||
EXTCODESIZE_TEST_SOURCE,
|
||||
"Warning: Your code or one of its dependencies uses the 'extcodesize' instruction,",
|
||||
BTreeMap::new(),
|
||||
SolcPipeline::Yul,
|
||||
false,
|
||||
Some(vec![Warning::ExtCodeSize]),
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
pub const TX_ORIGIN_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract TxOriginExample {
|
||||
function isOriginSender() public view returns (bool) {
|
||||
return tx.origin == msg.sender;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn tx_origin() {
|
||||
assert!(super::check_solidity_warning(
|
||||
TX_ORIGIN_TEST_SOURCE,
|
||||
"Warning: You are checking for 'tx.origin' in your code, which might lead to",
|
||||
BTreeMap::new(),
|
||||
SolcPipeline::Yul,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tx_origin_suppressed() {
|
||||
assert!(!super::check_solidity_warning(
|
||||
TX_ORIGIN_TEST_SOURCE,
|
||||
"Warning: You are checking for 'tx.origin' in your code, which might lead to",
|
||||
BTreeMap::new(),
|
||||
SolcPipeline::Yul,
|
||||
false,
|
||||
Some(vec![Warning::TxOrigin]),
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
pub const TX_ORIGIN_ASSEMBLY_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract TxOriginExample {
|
||||
function isOriginSender() public view returns (bool) {
|
||||
address txOrigin;
|
||||
address sender = msg.sender;
|
||||
|
||||
assembly {
|
||||
txOrigin := origin() // Get the transaction origin using the 'origin' instruction
|
||||
}
|
||||
|
||||
return txOrigin == sender;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn tx_origin_assembly() {
|
||||
assert!(super::check_solidity_warning(
|
||||
TX_ORIGIN_ASSEMBLY_TEST_SOURCE,
|
||||
"Warning: You are checking for 'tx.origin' in your code, which might lead to",
|
||||
BTreeMap::new(),
|
||||
SolcPipeline::Yul,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tx_origin_assembly_suppressed() {
|
||||
assert!(!super::check_solidity_warning(
|
||||
TX_ORIGIN_ASSEMBLY_TEST_SOURCE,
|
||||
"Warning: You are checking for 'tx.origin' in your code, which might lead to",
|
||||
BTreeMap::new(),
|
||||
SolcPipeline::Yul,
|
||||
false,
|
||||
Some(vec![Warning::TxOrigin]),
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn internal_function_pointer_argument() {
|
||||
let source_code = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract InternalFunctionPointerExample {
|
||||
function add(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
function executeOperation(
|
||||
function (uint256, uint256) internal pure returns (uint256) operation,
|
||||
uint256 a,
|
||||
uint256 b
|
||||
) private pure returns (uint256) {
|
||||
return operation(a, b);
|
||||
}
|
||||
|
||||
function testAdd(uint256 a, uint256 b) public pure returns (uint256) {
|
||||
return executeOperation(add, a, b);
|
||||
}
|
||||
|
||||
function testSub(uint256 a, uint256 b) public pure returns (uint256) {
|
||||
return executeOperation(sub, a, b);
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
assert!(super::check_solidity_warning(
|
||||
source_code,
|
||||
"Error: Internal function pointers are not supported in EVM legacy assembly pipeline.",
|
||||
BTreeMap::new(),
|
||||
SolcPipeline::EVMLA,
|
||||
true,
|
||||
None,
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn internal_function_pointer_stack() {
|
||||
let source_code = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract StackFunctionPointerExample {
|
||||
function add(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
function testAdd(uint256 a, uint256 b) public pure returns (uint256) {
|
||||
function (uint256, uint256) internal pure returns (uint256) operation = add;
|
||||
return operation(a, b);
|
||||
}
|
||||
|
||||
function testSub(uint256 a, uint256 b) public pure returns (uint256) {
|
||||
function (uint256, uint256) internal pure returns (uint256) operation = sub;
|
||||
return operation(a, b);
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
assert!(super::check_solidity_warning(
|
||||
source_code,
|
||||
"Error: Internal function pointers are not supported in EVM legacy assembly pipeline.",
|
||||
BTreeMap::new(),
|
||||
SolcPipeline::EVMLA,
|
||||
true,
|
||||
None,
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn internal_function_pointer_storage() {
|
||||
let source_code = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract StorageFunctionPointerExample {
|
||||
function add(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
function (uint256, uint256) internal pure returns (uint256) operation;
|
||||
bool private isOperationSet = false;
|
||||
|
||||
function setOperation(bool isAdd) public {
|
||||
if (isAdd) {
|
||||
operation = add;
|
||||
} else {
|
||||
operation = sub;
|
||||
}
|
||||
isOperationSet = true;
|
||||
}
|
||||
|
||||
function executeOperation(uint256 a, uint256 b) public view returns (uint256) {
|
||||
require(isOperationSet, "Operation not set");
|
||||
return operation(a, b);
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
assert!(super::check_solidity_warning(
|
||||
source_code,
|
||||
"Error: Internal function pointers are not supported in EVM legacy assembly pipeline.",
|
||||
BTreeMap::new(),
|
||||
SolcPipeline::EVMLA,
|
||||
true,
|
||||
None,
|
||||
)
|
||||
.expect("Test failure"));
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
//!
|
||||
//! The Solidity compiler unit tests.
|
||||
//!
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
mod factory_dependency;
|
||||
mod ir_artifacts;
|
||||
mod libraries;
|
||||
mod messages;
|
||||
mod optimizer;
|
||||
mod remappings;
|
||||
mod runtime_code;
|
||||
mod unsupported_opcodes;
|
||||
|
||||
pub(crate) use super::test_utils::*;
|
||||
@@ -0,0 +1,137 @@
|
||||
//!
|
||||
//! The Solidity compiler unit tests for the optimizer.
|
||||
//!
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
pub const SOURCE_CODE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.5.0;
|
||||
|
||||
contract Test {
|
||||
uint8 constant ARRAY_SIZE = 40;
|
||||
uint128 constant P = 257;
|
||||
uint128 constant MODULO = 1000000007;
|
||||
|
||||
function complex() public pure returns(uint64) {
|
||||
uint8[ARRAY_SIZE] memory array;
|
||||
// generate array where first half equals second
|
||||
for(uint8 i = 0; i < ARRAY_SIZE; i++) {
|
||||
array[i] = (i % (ARRAY_SIZE / 2)) * (255 / (ARRAY_SIZE / 2 - 1));
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
for(uint8 i = 0; i < ARRAY_SIZE/2; i++) {
|
||||
result = result && hash(array, 0, i + 1) == hash(array, ARRAY_SIZE/2, ARRAY_SIZE/2 + i + 1)
|
||||
&& hash(array, i, ARRAY_SIZE/2) == hash(array, i + ARRAY_SIZE/2, ARRAY_SIZE);
|
||||
}
|
||||
if (result) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function hash(uint8[ARRAY_SIZE] memory array, uint8 begin, uint8 end) private pure returns(uint128) {
|
||||
uint128 h = 0;
|
||||
for(uint8 i = begin; i < end; i++) {
|
||||
h = (h * P + array[i]) % MODULO;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn optimizer() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), SOURCE_CODE.to_owned());
|
||||
|
||||
let build_unoptimized = super::build_solidity(
|
||||
sources.clone(),
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
SolcPipeline::Yul,
|
||||
era_compiler_llvm_context::OptimizerSettings::none(),
|
||||
)
|
||||
.expect("Build failure");
|
||||
let build_optimized_for_cycles = super::build_solidity(
|
||||
sources.clone(),
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
SolcPipeline::Yul,
|
||||
era_compiler_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Build failure");
|
||||
let build_optimized_for_size = super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
SolcPipeline::Yul,
|
||||
era_compiler_llvm_context::OptimizerSettings::size(),
|
||||
)
|
||||
.expect("Build failure");
|
||||
|
||||
let size_when_unoptimized = build_unoptimized
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Missing field `contracts`")
|
||||
.get("test.sol")
|
||||
.expect("Missing file `test.sol`")
|
||||
.get("Test")
|
||||
.expect("Missing contract `test.sol:Test`")
|
||||
.evm
|
||||
.as_ref()
|
||||
.expect("Missing EVM data")
|
||||
.bytecode
|
||||
.as_ref()
|
||||
.expect("Missing bytecode")
|
||||
.object
|
||||
.len();
|
||||
let size_when_optimized_for_cycles = build_optimized_for_cycles
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Missing field `contracts`")
|
||||
.get("test.sol")
|
||||
.expect("Missing file `test.sol`")
|
||||
.get("Test")
|
||||
.expect("Missing contract `test.sol:Test`")
|
||||
.evm
|
||||
.as_ref()
|
||||
.expect("Missing EVM data")
|
||||
.bytecode
|
||||
.as_ref()
|
||||
.expect("Missing bytecode")
|
||||
.object
|
||||
.len();
|
||||
let size_when_optimized_for_size = build_optimized_for_size
|
||||
.contracts
|
||||
.as_ref()
|
||||
.expect("Missing field `contracts`")
|
||||
.get("test.sol")
|
||||
.expect("Missing file `test.sol`")
|
||||
.get("Test")
|
||||
.expect("Missing contract `test.sol:Test`")
|
||||
.evm
|
||||
.as_ref()
|
||||
.expect("Missing EVM data")
|
||||
.bytecode
|
||||
.as_ref()
|
||||
.expect("Missing bytecode")
|
||||
.object
|
||||
.len();
|
||||
|
||||
assert!(
|
||||
size_when_optimized_for_cycles < size_when_unoptimized,
|
||||
"Expected the cycles-optimized bytecode to be smaller than the unoptimized. Optimized: {}B, Unoptimized: {}B", size_when_optimized_for_cycles, size_when_unoptimized,
|
||||
);
|
||||
assert!(
|
||||
size_when_optimized_for_size < size_when_unoptimized,
|
||||
"Expected the size-optimized bytecode to be smaller than the unoptimized. Optimized: {}B, Unoptimized: {}B", size_when_optimized_for_size, size_when_unoptimized,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//!
|
||||
//! The Solidity compiler unit tests for remappings.
|
||||
//!
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
pub const CALLEE_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.4.16;
|
||||
|
||||
contract Callable {
|
||||
function f(uint a) public pure returns(uint) {
|
||||
return a * 2;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
pub const CALLER_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity >=0.4.16;
|
||||
|
||||
import "libraries/default/callable.sol";
|
||||
|
||||
contract Main {
|
||||
function main(Callable callable) public returns(uint) {
|
||||
return callable.f(5);
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn default() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("./test.sol".to_owned(), CALLER_TEST_SOURCE.to_owned());
|
||||
sources.insert("./callable.sol".to_owned(), CALLEE_TEST_SOURCE.to_owned());
|
||||
|
||||
let mut remappings = BTreeSet::new();
|
||||
remappings.insert("libraries/default/=./".to_owned());
|
||||
|
||||
super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
Some(remappings),
|
||||
SolcPipeline::Yul,
|
||||
era_compiler_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//!
|
||||
//! The Solidity compiler unit tests for runtime code.
|
||||
//!
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "runtimeCode is not supported")]
|
||||
fn default() {
|
||||
let source_code = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract A {}
|
||||
|
||||
contract Test {
|
||||
function main() public pure returns(bytes memory) {
|
||||
return type(A).runtimeCode;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), source_code.to_owned());
|
||||
|
||||
super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
SolcPipeline::Yul,
|
||||
era_compiler_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
//!
|
||||
//! The Solidity compiler unit tests for unsupported opcodes.
|
||||
//!
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "The `CODECOPY` instruction is not supported")]
|
||||
fn codecopy_yul_runtime() {
|
||||
let source_code = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract FixedCodeCopy {
|
||||
function copyCode() public view returns (bytes memory) {
|
||||
uint256 fixedCodeSize = 64;
|
||||
bytes memory code = new bytes(fixedCodeSize);
|
||||
|
||||
assembly {
|
||||
codecopy(add(code, 0x20), 0, fixedCodeSize)
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), source_code.to_owned());
|
||||
|
||||
super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
SolcPipeline::Yul,
|
||||
era_compiler_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
}
|
||||
|
||||
pub const CALLCODE_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract CallcodeTest {
|
||||
function testCallcode(address target, bytes4 signature, uint256 inputValue) public returns (bool) {
|
||||
bool success;
|
||||
|
||||
assembly {
|
||||
let input := mload(0x40)
|
||||
mstore(input, signature)
|
||||
mstore(add(input, 0x04), inputValue)
|
||||
|
||||
let callResult := callcode(gas(), target, 0, input, 0x24, 0, 0)
|
||||
|
||||
success := and(callResult, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "The `CALLCODE` instruction is not supported")]
|
||||
fn callcode_evmla() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), CALLCODE_TEST_SOURCE.to_owned());
|
||||
|
||||
super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
SolcPipeline::EVMLA,
|
||||
era_compiler_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "The `CALLCODE` instruction is not supported")]
|
||||
fn callcode_yul() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), CALLCODE_TEST_SOURCE.to_owned());
|
||||
|
||||
super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
SolcPipeline::Yul,
|
||||
era_compiler_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "The `PC` instruction is not supported")]
|
||||
fn pc_yul() {
|
||||
let source_code = r#"
|
||||
object "ProgramCounter" {
|
||||
code {
|
||||
datacopy(0, dataoffset("ProgramCounter_deployed"), datasize("ProgramCounter_deployed"))
|
||||
return(0, datasize("ProgramCounter_deployed"))
|
||||
}
|
||||
object "ProgramCounter_deployed" {
|
||||
code {
|
||||
function getPC() -> programCounter {
|
||||
programCounter := pc()
|
||||
}
|
||||
|
||||
let pcValue := getPC()
|
||||
sstore(0, pcValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
super::build_yul(source_code).expect("Test failure");
|
||||
}
|
||||
|
||||
pub const EXTCODECOPY_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract ExternalCodeCopy {
|
||||
function copyExternalCode(address target, uint256 codeSize) public view returns (bytes memory) {
|
||||
bytes memory code = new bytes(codeSize);
|
||||
|
||||
assembly {
|
||||
extcodecopy(target, add(code, 0x20), 0, codeSize)
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "The `EXTCODECOPY` instruction is not supported")]
|
||||
fn extcodecopy_evmla() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), EXTCODECOPY_TEST_SOURCE.to_owned());
|
||||
|
||||
super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
SolcPipeline::EVMLA,
|
||||
era_compiler_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "The `EXTCODECOPY` instruction is not supported")]
|
||||
fn extcodecopy_yul() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), EXTCODECOPY_TEST_SOURCE.to_owned());
|
||||
|
||||
super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
SolcPipeline::Yul,
|
||||
era_compiler_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
}
|
||||
|
||||
pub const SELFDESTRUCT_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract MinimalDestructible {
|
||||
address payable public owner;
|
||||
|
||||
constructor() {
|
||||
owner = payable(msg.sender);
|
||||
}
|
||||
|
||||
function destroy() public {
|
||||
require(msg.sender == owner, "Only the owner can call this function.");
|
||||
selfdestruct(owner);
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "The `SELFDESTRUCT` instruction is not supported")]
|
||||
fn selfdestruct_evmla() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), SELFDESTRUCT_TEST_SOURCE.to_owned());
|
||||
|
||||
super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
SolcPipeline::EVMLA,
|
||||
era_compiler_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "The `SELFDESTRUCT` instruction is not supported")]
|
||||
fn selfdestruct_yul() {
|
||||
let mut sources = BTreeMap::new();
|
||||
sources.insert("test.sol".to_owned(), SELFDESTRUCT_TEST_SOURCE.to_owned());
|
||||
|
||||
super::build_solidity(
|
||||
sources,
|
||||
BTreeMap::new(),
|
||||
None,
|
||||
SolcPipeline::Yul,
|
||||
era_compiler_llvm_context::OptimizerSettings::cycles(),
|
||||
)
|
||||
.expect("Test failure");
|
||||
}
|
||||
Reference in New Issue
Block a user