Compare commits

..

28 Commits

Author SHA1 Message Date
Yuri Volkov 48a019e0ad Fix: add llvm- prefix to llvm version tag (#225)
Needed both for differentiating them from regular releases and for
filtering the tags in automation
2025-02-21 12:05:40 +01:00
xermicus 17a2d2f9f2 update README.md (#223)
Signed-off-by: xermicus <cyrill@parity.io>
2025-02-21 10:51:40 +01:00
Yuri Volkov 6ad7908c5e Using workflow_dispatch for LLVM releases (#222) 2025-02-20 20:00:33 +01:00
Yuri Volkov 840a736fc5 LLVM release workflows (#207)
Co-authored-by: xermicus <cyrill@parity.io>
2025-02-20 12:30:52 +01:00
xermicus 89cdfefab4 remove some dead code leftovers (#218)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-02-19 10:41:32 +01:00
xermicus 6c2c633651 release resolc v0.1.0-dev.11 (#214)
Signed-off-by: xermicus <cyrill@parity.io>
2025-02-18 08:08:20 +01:00
xermicus a73b0925c6 ci: do not run bun tests (#215) 2025-02-17 17:04:53 +01:00
xermicus ee650cf03a ci: use upstream evm for tests (#213)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-02-17 11:33:30 +01:00
Evgeny Snitko c2210442b6 Release workflow (#201)
Co-authored-by: xermicus <cyrill@parity.io>
2025-02-17 11:09:10 +01:00
xermicus cb268850a9 llvm-context: remove the linear memory pointer indirection (#211) 2025-02-13 13:21:43 +01:00
xermicus f3a86588f3 update dependencies (#210)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-02-12 13:12:09 +01:00
xermicus 7233738f45 release resolc v0.1.0-dev.10 (#209)
Signed-off-by: xermicus <cyrill@parity.io>
2025-02-11 15:51:52 +01:00
Sebastian Miasojed 79ec4dd04b Add all resolc dependencies to resolc_web.js file (#176) 2025-02-11 11:55:24 +01:00
xermicus 374563bbe5 integration: add function pointer integration test (#205)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-02-10 18:50:12 +01:00
xermicus a921e425b4 CI: fix and pin geth (#206) 2025-02-10 17:39:44 +01:00
xermicus 60fc09f787 llvm-context: disable call re-entrancy for send and transfer (#196) 2025-02-06 16:49:50 +01:00
Sebastian Miasojed 10b8ff989c Align emscripten compilation options (#180) 2025-02-06 00:37:10 +01:00
xermicus 157647d5c1 remove superfluous warning and clippy allows (#194)
Signed-off-by: xermicus <cyrill@parity.io>
2025-02-05 12:44:29 +01:00
xermicus 35419e8202 solidity: update custom warnings and version validation (#193) 2025-02-05 11:42:35 +01:00
xermicus de3e7bf253 integration: add create2 test case with duplicate salt (#188)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-02-04 14:04:14 +01:00
xermicus 9fb24b607d disable werror (#191)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-02-04 12:43:56 +01:00
xermicus ba7310fdff integration: bugfix blob cache (#192)
Signed-off-by: xermicus <cyrill@parity.io>
2025-02-04 12:11:25 +01:00
dependabot[bot] 4ef495beea Bump openssl from 0.10.68 to 0.10.70 (#190)
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.68 to 0.10.70.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.68...openssl-v0.10.70)

---
updated-dependencies:
- dependency-name: openssl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-04 07:28:29 +01:00
xermicus 8ed689e7ec solidity: various small resolc fixes (#189) 2025-02-03 16:16:54 +01:00
xermicus bfda465c32 remove support for legacy evm assembly (#186) 2025-02-03 14:13:43 +01:00
xermicus ab90af49df Solidity: add --libraries to sources (#187)
* bugfix: add libraries to sources

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-01-31 18:08:11 +01:00
Sebastian Miasojed 8201401fef Fix stack overflow issue (#184) 2025-01-31 14:31:34 +01:00
xermicus 1a8a7926e9 implement the coinbase opcode (#179)
Signed-off-by: xermicus <cyrill@parity.io>
2025-01-29 15:24:45 +01:00
125 changed files with 3244 additions and 7446 deletions
+2 -3
View File
@@ -12,7 +12,6 @@ rustflags = [
"-Clink-arg=-sALLOW_TABLE_GROWTH=1", "-Clink-arg=-sALLOW_TABLE_GROWTH=1",
"-Clink-arg=--js-library=js/embed/soljson_interface.js", "-Clink-arg=--js-library=js/embed/soljson_interface.js",
"-Clink-arg=--pre-js=js/embed/pre.js", "-Clink-arg=--pre-js=js/embed/pre.js",
"-Clink-arg=-sNODEJS_CATCH_EXIT=0", "-Clink-arg=-sSTACK_SIZE=128kb",
"-Clink-arg=-sDISABLE_EXCEPTION_CATCHING=0", "-Clink-arg=-sNODEJS_CATCH_EXIT=0"
"-Copt-level=3"
] ]
+1 -16
View File
@@ -9,7 +9,6 @@ on:
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
REVIVE_WASM_INSTALL_DIR: ${{ github.workspace }}/target/wasm32-unknown-emscripten/release REVIVE_WASM_INSTALL_DIR: ${{ github.workspace }}/target/wasm32-unknown-emscripten/release
BUN_VERSION: 1.1.43
jobs: jobs:
build-revive-wasm: build-revive-wasm:
@@ -87,6 +86,7 @@ jobs:
path: | path: |
${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.js ${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.js
${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.wasm ${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.wasm
${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc_web.js
retention-days: 1 retention-days: 1
test-revive-wasm: test-revive-wasm:
@@ -112,21 +112,6 @@ jobs:
with: with:
node-version: "20" node-version: "20"
- name: Install Bun on Windows
if: runner.os == 'Windows'
run: |
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
iex (new-object net.webclient).downloadstring('https://get.scoop.sh')
scoop install bun@${{ env.BUN_VERSION }}
scoop install wget
Join-Path (Resolve-Path ~).Path "scoop\shims" >> $Env:GITHUB_PATH
- name: Install Bun on macOS and Linux
if: runner.os != 'Windows'
run: |
curl -fsSL https://bun.sh/install | bash -s bun-v${{ env.BUN_VERSION }}
echo "$HOME/.bun/bin" >> $GITHUB_PATH
- name: Install packages - name: Install packages
run: npm install run: npm install
+167
View File
@@ -0,0 +1,167 @@
name: Release LLVM
on:
workflow_dispatch:
inputs:
llvm_version:
type: string
required: true
description: llvm version in "x.x.x" format, e.g. "18.1.8"
env:
CARGO_TERM_COLOR: always
jobs:
create-release:
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
version: ${{ steps.resolve-version.outputs.version }}
steps:
- id: resolve-version
run: |
echo "version=llvm-${{ inputs.llvm_version }}-revive.${GITHUB_SHA:0:7}" >> $GITHUB_OUTPUT
- name: create release
uses: softprops/action-gh-release@v2
with:
name: "LLVM binaries release: ${{ steps.resolve-version.outputs.version }}"
body: "This release includes binaries of LLVM, used to compile revive itself"
make_latest: "false"
tag_name: ${{ steps.resolve-version.outputs.version }}
build-macos:
strategy:
matrix:
os: [macos-14, macos-13]
include:
- os: macos-13
arch: x64
- os: macos-14
arch: arm64
needs: create-release
runs-on: ${{ matrix.os }}
name: "build-macos-${{ matrix.arch }}"
env:
RUST_LOG: trace
permissions:
contents: write # for uploading assets to release
steps:
- uses: actions/checkout@v4
- name: install macos deps
run: |
brew install ninja
- name: versions
run: |
rustup show
cargo --version
cmake --version
echo "bash:" && bash --version
echo "ninja:" && ninja --version
echo "clang:" && clang --version
- name: Build LLVM
run: |
make install-llvm
- name: clean
# check removed files
run: |
cd target-llvm/gnu/target-final/bin/
rm diagtool llvm-libtool-darwin llvm-lipo llvm-pdbutil llvm-dwarfdump llvm-nm llvm-readobj llvm-cfi-verify \
sancov llvm-debuginfo-analyzer llvm-objdump llvm-profgen llvm-extract llvm-jitlink llvm-c-test llvm-gsymutil llvm-dwp \
dsymutil llvm-dwarfutil llvm-exegesis lli clang-rename bugpoint clang-extdef-mapping clang-refactor c-index-test \
llvm-reduce llvm-lto clang-linker-wrapper llc llvm-lto2
- name: package artifacts
run: |
tar -czf "${{ needs.create-release.outputs.version }}-macos-${{ matrix.arch }}.tar.gz" target-llvm/gnu/target-final
- name: upload archive to release
uses: softprops/action-gh-release@v2
with:
make_latest: "false"
tag_name: ${{ needs.create-release.outputs.version }}
files: |
${{ needs.create-release.outputs.version }}-macos-${{ matrix.arch }}.tar.gz
build-linux-all:
needs: create-release
runs-on: parity-large
env:
RUST_LOG: trace
permissions:
contents: write # for uploading assets to release
steps:
- uses: actions/checkout@v4
- name: install linux deps
run: |
sudo apt-get update && sudo apt-get install -y cmake ninja-build curl git libssl-dev pkg-config clang lld musl
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
components: rust-src
target: wasm32-unknown-emscripten
rustflags: ""
- name: versions
run: |
rustup show
cargo --version
cmake --version
echo "bash:" && bash --version
echo "ninja:" && ninja --version
echo "clang:" && clang --version
- name: Build host LLVM
run: |
make install-llvm
- name: Build gnu LLVM
run: |
revive-llvm clone
revive-llvm build --llvm-projects lld --llvm-projects clang
- name: Build musl LLVM
run: |
revive-llvm --target-env musl build --llvm-projects lld --llvm-projects clang
- name: Build emscripten LLVM
run: |
revive-llvm --target-env emscripten clone
source emsdk/emsdk_env.sh
revive-llvm --target-env emscripten build --llvm-projects lld
- name: clean
# check removed files
run: |
for target in gnu emscripten musl; do
cd target-llvm/${target}/target-final/bin/
rm -rf diagtool llvm-libtool-darwin llvm-lipo llvm-pdbutil llvm-dwarfdump llvm-nm llvm-readobj llvm-cfi-verify \
sancov llvm-debuginfo-analyzer llvm-objdump llvm-profgen llvm-extract llvm-jitlink llvm-c-test llvm-gsymutil llvm-dwp \
dsymutil llvm-dwarfutil llvm-exegesis lli clang-rename bugpoint clang-extdef-mapping clang-refactor c-index-test \
llvm-reduce llvm-lto clang-linker-wrapper llc llvm-lto2 llvm-otool llvm-readelf
cd -
done
- name: package artifacts
run: |
tar -czf "${{ needs.create-release.outputs.version }}-x86_64-linux-gnu-linux.tar.gz" target-llvm/gnu/target-final
tar -czf "${{ needs.create-release.outputs.version }}-x86_64-linux-musl.tar.gz" target-llvm/musl/target-final
tar -czf "${{ needs.create-release.outputs.version }}-wasm32-unknown-emscripten.tar.gz" target-llvm/emscripten/target-final
- name: upload archive to release
uses: softprops/action-gh-release@v2
with:
make_latest: "false"
tag_name: ${{ needs.create-release.outputs.version }}
files: |
${{ needs.create-release.outputs.version }}-x86_64-linux-gnu-linux.tar.gz
${{ needs.create-release.outputs.version }}-x86_64-linux-musl.tar.gz
${{ needs.create-release.outputs.version }}-wasm32-unknown-emscripten.tar.gz
+347
View File
@@ -0,0 +1,347 @@
name: Release
run-name: Release ${{ github.ref_name }}
on:
push:
branches:
- 'main'
concurrency:
group: ${{ github.ref }}-${{ github.workflow }}
cancel-in-progress: true
env:
#rust-musl-cross:x86_64-musl
RUST_MUSL_CROSS_IMAGE: messense/rust-musl-cross@sha256:68b86bc7cb2867259e6b233415a665ff4469c28b57763e78c3bfea1c68091561
jobs:
#
#
#
tag:
runs-on: ubuntu-24.04
permissions:
contents: write
outputs:
TAG: ${{ steps.versions.outputs.TAG }}
PKG_VER: ${{ steps.versions.outputs.PKG_VER }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-tags: 'true'
fetch-depth: 0
- name: Versions
id: versions
run: |
export CURRENT_TAG=$(git describe --tags --abbrev=0 --exclude "llvm-*")
export PKG_VER=v$(cat Cargo.toml | grep -A 5 package] | grep version | cut -d '=' -f 2 | tr -d '"' | tr -d " ")
echo "Current tag $CURRENT_TAG"
echo "Package version $PKG_VER"
#
echo "PKG_VER=$PKG_VER" >> $GITHUB_OUTPUT
if [ $CURRENT_TAG == $PKG_VER ];
then
echo "Tag is up to date. Nothing to do.";
export TAG=old;
else
echo "Tag was updated.";
export TAG=new;
fi
echo "TAG=$TAG" >> $GITHUB_OUTPUT
#
#
#
build-linux-all:
if: ${{ needs.tag.outputs.TAG == 'new' }}
runs-on: parity-large
needs: [tag]
steps:
- uses: actions/checkout@v4
- name: install linux deps
run: |
sudo apt-get update && sudo apt-get install -y cmake ninja-build \
curl git libssl-dev pkg-config clang lld musl
- name: Install Rust stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
components: rust-src
target: wasm32-unknown-emscripten
- name: versions
run: |
rustup show
cargo --version
cmake --version
echo "bash:" && bash --version
echo "ninja:" && ninja --version
echo "clang:" && clang --version
- name: build revive-llvm
run: make install-llvm-builder
# musl LLVM
- name: llvm-musl-cache restore
id: llvm-musl-cache
uses: actions/cache/restore@v4
with:
path: target-llvm/musl/target-final
key: llvm-linux-musl-${{ hashFiles('crates/solidity/**') }}
- name: Build musl LLVM
if: steps.llvm-musl-cache.outputs.cache-hit != 'true'
run: |
revive-llvm --target-env musl clone
revive-llvm --target-env musl build --llvm-projects lld --llvm-projects clang
- name: llvm-musl-cache save
if: steps.llvm-musl-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: target-llvm/musl/target-final
key: llvm-linux-musl-${{ hashFiles('crates/solidity/**') }}
# emscripten LLVM
- name: llvm-emscripten-cache restore
id: llvm-emscripten-cache
uses: actions/cache/restore@v4
with:
path: |
target-llvm/emscripten/target-final
emsdk
key: llvm-linux-emscripten-${{ hashFiles('crates/solidity/**') }}
- name: Build emscripten LLVM
if: steps.llvm-emscripten-cache.outputs.cache-hit != 'true'
run: |
revive-llvm --target-env emscripten clone
source emsdk/emsdk_env.sh
revive-llvm --target-env emscripten build --llvm-projects lld
- name: llvm-emscripten-cache save
if: steps.llvm-emscripten-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: |
target-llvm/emscripten/target-final
emsdk
key: llvm-linux-emscripten-${{ hashFiles('crates/solidity/**') }}
# Build revive
- name: build musl
run: |
mkdir resolc-out
docker run -v $PWD:/opt/revive $RUST_MUSL_CROSS_IMAGE /bin/bash -c "
cd /opt/revive
apt update && apt upgrade -y && apt install -y pkg-config
export LLVM_SYS_181_PREFIX=/opt/revive/target-llvm/musl/target-final
make install-bin
cp /root/.cargo/bin/resolc /opt/revive/resolc-out/resolc
"
- name: check musl
run: |
mkdir solc
curl -sSLo solc/solc https://github.com/ethereum/solidity/releases/download/v0.8.28/solc-static-linux
chmod +x solc/solc
PATH=$PWD/solc:$PATH
result=$(./resolc-out/resolc --bin crates/integration/contracts/flipper.sol)
echo $result
if [[ $result == *'0x50564d'* ]]; then exit 0; else exit 1; fi
- name: Set Up Node.js
uses: actions/setup-node@v3
with:
node-version: "20"
- name: build wasm
run: |
export LLVM_SYS_181_PREFIX=$PWD/target-llvm/musl/target-final
export REVIVE_LLVM_TARGET_PREFIX=$PWD/target-llvm/emscripten/target-final
source emsdk/emsdk_env.sh
rustup target add wasm32-unknown-emscripten
make install-wasm
- name: check wasm
run: |
curl -sSLo solc/soljson.js https://github.com/ethereum/solidity/releases/download/v0.8.28/soljson.js
node -e "
const soljson = require('solc/soljson');
const createRevive = require('./target/wasm32-unknown-emscripten/release/resolc.js');
const compiler = createRevive();
compiler.soljson = soljson;
const standardJsonInput =
{
language: 'Solidity',
sources: {
'MyContract.sol': {
content: 'pragma solidity ^0.8.0; contract MyContract { function greet() public pure returns (string memory) { return \'Hello\'; } }',
},
},
settings: { optimizer: { enabled: false } }
};
compiler.writeToStdin(JSON.stringify(standardJsonInput));
compiler.callMain(['--standard-json']);
// Collect output
const stdout = compiler.readFromStdout();
const stderr = compiler.readFromStderr();
if (stderr) { console.error(stderr); process.exit(1); }
let out = JSON.parse(stdout);
let bytecode = out.contracts['MyContract.sol']['MyContract'].evm.bytecode.object
console.log(bytecode);
if(!bytecode.startsWith('50564d')) { process.exit(1); }
"
- uses: actions/upload-artifact@v4
with:
name: revive-wasm
path: |
./target/wasm32-unknown-emscripten/release/resolc.js
./target/wasm32-unknown-emscripten/release/resolc.wasm
./target/wasm32-unknown-emscripten/release/resolc_web.js
retention-days: 1
- uses: actions/upload-artifact@v4
with:
name: revive-linux
path: |
./resolc-out/resolc
retention-days: 1
#
#
#
create-release:
needs: [tag, build-linux-all]
runs-on: ubuntu-24.04
permissions:
contents: write
outputs:
upload_url: ${{ steps.create_release.outputs.result }}
steps:
- uses: actions/checkout@v4
- name: Create/update tag
id: tag
uses: actions/github-script@v7
with:
result-encoding: string
script: |
await github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'refs/tags/${{ needs.tag.outputs.PKG_VER }}',
sha: context.sha
})
- name: get relese notes
id: get-notes
run: |
{
echo 'releaseNotes<<EOF'
sed '/^## ${{ needs.tag.outputs.PKG_VER }}/,/^## v/!d' CHANGELOG.md | sed -e '1d' -e '$d'
echo EOF
} >> "$GITHUB_OUTPUT"
- name: Create release
id: create_release
env:
releaseNotes: ${{ steps.get-notes.outputs.releaseNotes }}
version: ${{ needs.tag.outputs.PKG_VER }}
uses: actions/github-script@v7
with:
result-encoding: string
script: |
let response = await github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: process.env.version,
name: process.env.version,
body: process.env.releaseNotes,
draft: true,
prerelease: true
});
console.log(response);
return response.data.upload_url;
- name: Log
run: |
echo "tag result: ${{ needs.tag.outputs.TAG }}"
echo "pkg version: ${{ needs.tag.outputs.PKG_VER }}"
#
#
#
upload-assets:
runs-on: ubuntu-24.04
needs: [create-release]
steps:
- name: Download Artifact
uses: actions/download-artifact@v4
with:
name: revive-wasm
path: resolc/
- name: Download Artifact
uses: actions/download-artifact@v4
with:
name: revive-linux
path: resolc/
- name: upload resolc
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ./resolc/resolc
asset_name: resolc-static-linux
asset_content_type: application/octet-stream
- name: upload resolc.js
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ./resolc/resolc.js
asset_name: resolc.js
asset_content_type: application/octet-stream
- name: upload resolc.wasm
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ./resolc/resolc.wasm
asset_name: resolc.wasm
asset_content_type: application/octet-stream
- name: upload resolc_web.js
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ./resolc/resolc_web.js
asset_name: resolc_web.js
asset_content_type: application/octet-stream
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
tar Jxf llvm.tar.xz -C llvm18/ tar Jxf llvm.tar.xz -C llvm18/
echo "LLVM_SYS_181_PREFIX=$(pwd)/llvm18" >> $GITHUB_ENV echo "LLVM_SYS_181_PREFIX=$(pwd)/llvm18" >> $GITHUB_ENV
- name: Install apt dependencies - name: Install geth
run: | run: |
sudo add-apt-repository -y ppa:ethereum/ethereum sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt update sudo apt update
+44
View File
@@ -2,6 +2,50 @@
## Unreleased ## Unreleased
This is a development pre-release.
Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
## v0.1.0-dev.11
This is a development pre-release.
Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
### Added
### Changed
### Fixed
- A bug causing incorrect loads from the emulated EVM linear memory.
- A missing integer truncate after switching to 64bit.
## v0.1.0-dev.10
This is a development pre-release.
Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
### Added
- Support for the `coinbase` opcode.
- The resolc web JS version.
### Changed
- Missing the `--overwrite` flag emits an error instead of a warning.
- The `resolc` executable prints the help by default.
- Removed support for legacy EVM assembly (EVMLA) translation.
- integration: identify cached code blobs on source code to fix potential confusions.
- Setting base, include or allow paths in emscripten is now a hard error.
- Employ a heuristic to detect `address.transfer` and `address.send` calls.
If detected, the re-entrant call flag is not set and 0 deposit limit is endowed.
### Fixed
- Solidity: Add the solc `--libraries` files to sources.
- A data race in tests.
- Fix `broken pipe` errors.
- llvm-builder: Allow warnings.
- solidity: Fix the custom compiler warning messages.
## v0.1.0-dev.9 ## v0.1.0-dev.9
This is a development pre-release. This is a development pre-release.
Generated
+806 -655
View File
File diff suppressed because it is too large Load Diff
+30 -31
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/*"] members = ["crates/*"]
[workspace.package] [workspace.package]
version = "0.1.0-dev.9" version = "0.1.0-dev.11"
authors = [ authors = [
"Cyrill Leutwiler <cyrill@parity.io>", "Cyrill Leutwiler <cyrill@parity.io>",
"Parity Technologies <admin@parity.io>", "Parity Technologies <admin@parity.io>",
@@ -14,48 +14,47 @@ repository = "https://github.com/paritytech/revive"
rust-version = "1.81.0" rust-version = "1.81.0"
[workspace.dependencies] [workspace.dependencies]
revive-benchmarks = { version = "0.1.0-dev.9", path = "crates/benchmarks" } revive-benchmarks = { version = "0.1.0-dev.11", path = "crates/benchmarks" }
revive-builtins = { version = "0.1.0-dev.9", path = "crates/builtins" } revive-builtins = { version = "0.1.0-dev.11", path = "crates/builtins" }
revive-common = { version = "0.1.0-dev.9", path = "crates/common" } revive-common = { version = "0.1.0-dev.11", path = "crates/common" }
revive-differential = { version = "0.1.0-dev.9", path = "crates/differential" } revive-differential = { version = "0.1.0-dev.11", path = "crates/differential" }
revive-integration = { version = "0.1.0-dev.9", path = "crates/integration" } revive-integration = { version = "0.1.0-dev.11", path = "crates/integration" }
revive-linker = { version = "0.1.0-dev.9", path = "crates/linker" } revive-linker = { version = "0.1.0-dev.11", path = "crates/linker" }
lld-sys = { version = "0.1.0-dev.9", path = "crates/lld-sys" } lld-sys = { version = "0.1.0-dev.11", path = "crates/lld-sys" }
revive-llvm-context = { version = "0.1.0-dev.9", path = "crates/llvm-context" } revive-llvm-context = { version = "0.1.0-dev.11", path = "crates/llvm-context" }
revive-runtime-api = { version = "0.1.0-dev.9", path = "crates/runtime-api" } revive-runtime-api = { version = "0.1.0-dev.11", path = "crates/runtime-api" }
revive-runner = { version = "0.1.0-dev.9", path = "crates/runner" } revive-runner = { version = "0.1.0-dev.11", path = "crates/runner" }
revive-solidity = { version = "0.1.0-dev.9", path = "crates/solidity" } revive-solidity = { version = "0.1.0-dev.11", path = "crates/solidity" }
revive-stdlib = { version = "0.1.0-dev.9", path = "crates/stdlib" } revive-stdlib = { version = "0.1.0-dev.11", path = "crates/stdlib" }
revive-build-utils = { version = "0.1.0-dev.9", path = "crates/build-utils" } revive-build-utils = { version = "0.1.0-dev.11", path = "crates/build-utils" }
hex = "0.4.3" hex = "0.4.3"
cc = "1.0" cc = "1.2"
libc = "0.2.169" libc = "0.2.169"
tempfile = "3.8" tempfile = "3.17"
anyhow = "1.0" anyhow = "1.0"
semver = { version = "1.0", features = ["serde"] } semver = { version = "1.0", features = ["serde"] }
itertools = "0.14" itertools = "0.14"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["arbitrary_precision"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] }
regex = "1.10" regex = "1.10"
once_cell = "1.19" once_cell = "1.20"
num = "0.4.3" num = "0.4.3"
sha1 = "0.10" sha1 = "0.10"
sha3 = "0.10" sha3 = "0.10"
md5 = "0.7.0"
thiserror = "2.0" thiserror = "2.0"
which = "7.0" which = "7.0"
path-slash = "0.2" path-slash = "0.2"
rayon = "1.8" rayon = "1.8"
clap = { version = "4", default-features = false, features = ["derive"] } clap = { version = "4", default-features = false, features = ["derive"] }
polkavm-common = "0.19.0" polkavm-common = "0.21.0"
polkavm-linker = "0.19.0" polkavm-linker = "0.21.0"
polkavm-disassembler = "0.19.0" polkavm-disassembler = "0.21.0"
polkavm = "0.19.0" polkavm = "0.21.0"
alloy-primitives = { version = "0.8.19", features = ["serde"] } alloy-primitives = { version = "0.8.21", features = ["serde"] }
alloy-sol-types = "0.8.19" alloy-sol-types = "0.8.21"
alloy-genesis = "0.9.2" alloy-genesis = "0.11.1"
alloy-serde = "0.9.2" alloy-serde = "0.11.1"
env_logger = { version = "0.11.6", default-features = false } env_logger = { version = "0.11.6", default-features = false }
serde_stacker = "0.1.11" serde_stacker = "0.1.11"
criterion = { version = "0.5.1", features = ["html_reports"] } criterion = { version = "0.5.1", features = ["html_reports"] }
@@ -65,15 +64,15 @@ downloader = "0.2.8"
flate2 = "1.0.35" flate2 = "1.0.35"
fs_extra = "1.3.0" fs_extra = "1.3.0"
num_cpus = "1" num_cpus = "1"
tar = "0.4.43" tar = "0.4"
toml = "0.8.19" toml = "0.8"
assert_cmd = "2.0.16" assert_cmd = "2.0"
assert_fs = "1.1.2" assert_fs = "1.1"
# polkadot-sdk and friends # polkadot-sdk and friends
codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" } codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" }
scale-info = { version = "2.11.6", default-features = false } scale-info = { version = "2.11.6", default-features = false }
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "4302f74f7874e6a894578731142a7b310a1449b0" } polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "274a781e8ca1a9432c7ec87593bd93214abbff50" }
# llvm # llvm
[workspace.dependencies.inkwell] [workspace.dependencies.inkwell]
+2 -1
View File
@@ -30,6 +30,7 @@ install-npm:
install-wasm: install-npm install-wasm: install-npm
cargo build --target wasm32-unknown-emscripten -p revive-solidity --release --no-default-features cargo build --target wasm32-unknown-emscripten -p revive-solidity --release --no-default-features
npm run build:package
install-llvm-builder: install-llvm-builder:
cargo install --path crates/llvm-builder cargo install --path crates/llvm-builder
@@ -42,7 +43,7 @@ format:
cargo fmt --all --check cargo fmt --all --check
clippy: clippy:
cargo clippy --all-features --workspace --tests --benches -- --deny warnings --allow dead_code cargo clippy --all-features --workspace --tests --benches -- --deny warnings
machete: machete:
cargo install cargo-machete cargo install cargo-machete
+43 -17
View File
@@ -1,9 +1,9 @@
![CI](https://github.com/paritytech/revive/actions/workflows/rust.yml/badge.svg) ![CI](https://github.com/paritytech/revive/actions/workflows/rust.yml/badge.svg)
[![Docs](https://img.shields.io/badge/Docs-contracts.polkadot.io-brightgreen.svg)](https://contracts.polkadot.io) [![Docs](https://img.shields.io/badge/Docs-contracts.polkadot.io-brightgreen.svg)](https://contracts.polkadot.io/revive_compiler/)
# revive # revive
YUL and EVM assembly recompiler to LLVM, targetting RISC-V on [PolkaVM](https://github.com/koute/polkavm). YUL recompiler to LLVM, targetting RISC-V on [PolkaVM](https://github.com/koute/polkavm).
Visit [contracts.polkadot.io](https://contracts.polkadot.io) to learn more about contracts on Polkadot! Visit [contracts.polkadot.io](https://contracts.polkadot.io) to learn more about contracts on Polkadot!
@@ -14,10 +14,7 @@ This is experimental software in active development and not ready just yet for p
Discussion around the development is hosted on the [Polkadot Forum](https://forum.polkadot.network/t/contracts-update-solidity-on-polkavm/6949#a-new-solidity-compiler-1). Discussion around the development is hosted on the [Polkadot Forum](https://forum.polkadot.network/t/contracts-update-solidity-on-polkavm/6949#a-new-solidity-compiler-1).
## Installation ## Installation
Please consult [the documentation](https://contracts.polkadot.io/revive_compiler/installation) for installation instructions.
`resolc` depends on the [solc](https://github.com/ethereum/solidity) binary installed on your system.
Download and install the `resolc` frontend executable for your platform from our [releases](https://github.com/paritytech/revive/releases).
## Building from source ## Building from source
@@ -25,22 +22,46 @@ Building revive requires a [stable Rust installation](https://rustup.rs/) and a
### LLVM ### LLVM
`revive` depends on a custom build of LLVM `v18.1.8` with the RISC-V _embedded_ target, including the `compiler-rt` builtins. Use the provided [revive-llvm](crates/llvm-builder/README.md) utility to compile a compatible LLVM build locally and point `$LLVM_SYS_181_PREFIX` to the installation afterwards. `revive` depends on a custom build of LLVM `v18.1.8` with the RISC-V _embedded_ target, including the `compiler-rt` builtins. You can either download a build from our releases (recommended for older hardware) or build it from source.
<details>
<summary>Download from our LLVM releases</summary>
Download the [latest LLVM build](https://github.com/paritytech/revive/releases?q=LLVM+binaries+release&expanded=true) from our releases.
> **MacOS** users need to clear the `downloaded` attribute from all binaries after extracting the archive:
> ```sh
> xattr -rc </path/to/the/extracted/archive>/target-llvm/gnu/target-final/bin/*
> ```
After extracting the archive, point `$LLVM_SYS_181_PREFIX` to it:
```sh
export LLVM_SYS_181_PREFIX=</path/to/the/extracted/archive>/target-llvm/gnu/target-final
```
</details>
<details>
<summary>Building from source</summary>
Use the provided [revive-llvm](crates/llvm-builder/README.md) utility to compile a compatible LLVM build locally and point `$LLVM_SYS_181_PREFIX` to the installation afterwards.
The `Makefile` provides a shortcut target to obtain a compatible LLVM build: The `Makefile` provides a shortcut target to obtain a compatible LLVM build:
```bash ```sh
make install-llvm make install-llvm
export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final
``` ```
</details>
### The `resolc` Solidity frontend ### The `resolc` Solidity frontend
To build the `resolc` Solidity frontend executable, make sure you have obtained a compatible LLVM build using [revive-llvm](crates/llvm-builder/README.md) and did export the `LLVM_SYS_181_PREFIX` environment variable pointing to it (see [above](#LLVM)). To build the `resolc` Solidity frontend executable, make sure you have obtained a compatible LLVM build and did export the `LLVM_SYS_181_PREFIX` environment variable pointing to it (see [above](#LLVM)).
To install the `resolc` Solidity frontend executable: To install the `resolc` Solidity frontend executable:
```bash ```sh
make install-bin make install-bin
resolc --version resolc --version
``` ```
@@ -49,7 +70,10 @@ resolc --version
Cross-compile the `resolc.js` frontend executable to Wasm for running it in a Node.js or browser environment. The `REVIVE_LLVM_TARGET_PREFIX` environment variable is used to control the target environment LLVM dependency. Cross-compile the `resolc.js` frontend executable to Wasm for running it in a Node.js or browser environment. The `REVIVE_LLVM_TARGET_PREFIX` environment variable is used to control the target environment LLVM dependency.
```bash <details>
<summary>Instructions for cross-compilation to wasm32-unknown-emscripten</summary>
```sh
# Build the host LLVM dependency with PolkaVM target support # Build the host LLVM dependency with PolkaVM target support
make install-llvm make install-llvm
export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final
@@ -65,21 +89,23 @@ make install-wasm
make test-wasm make test-wasm
``` ```
### Development </details>
## Development
Please consult the [Makefile](Makefile) targets to learn how to run tests and benchmarks. Please consult the [Makefile](Makefile) targets to learn how to run tests and benchmarks.
Ensure that your branch passes `make test` locally when submitting a pull request. Ensure that your branch passes `make test` locally when submitting a pull request.
## Design overview ### Design overview
See the [relevant section in our documentation](https://contracts.polkadot.io/revive_compiler/architecture) to learn more about how the compiler works.
`revive` uses [solc](https://github.com/ethereum/solidity/), the Ethereum Solidity compiler, as the [Solidity frontend](crates/solidity/src/lib.rs) to process smart contracts written in Solidity. The YUL IR code (or legacy EVM assembly as a fallback for older `solc` versions) emitted by `solc` is then translated to LLVM IR, targetting [Polkadots `revive` pallet](https://docs.rs/pallet-revive/latest/pallet_revive/trait.SyscallDoc.html). [Frontend](https://github.com/matter-labs/era-compiler-solidity) and [code generator](https://github.com/matter-labs/era-compiler-llvm-context) are based of ZKSync `zksolc` (the project started as a fork of the era compiler).
[Frontend](https://github.com/matter-labs/era-compiler-solidity) and [code generator](https://github.com/matter-labs/era-compiler-llvm-context) are based of ZKSync `zksolc`.
## Tests ### Tests
Before running the tests, ensure that Geth (Go Ethereum) is installed on your system. Follow the installation guide here: [Installing Geth](https://geth.ethereum.org/docs/getting-started/installing-geth). Before running the tests, ensure that Geth (Go Ethereum) is installed on your system. Follow the installation guide here: [Installing Geth](https://geth.ethereum.org/docs/getting-started/installing-geth).
Once Geth is installed, you can run the tests using the following command: Once Geth is installed, you can run the tests using the following command:
```bash ```sh
make test make test
``` ```
+11 -5
View File
@@ -4,8 +4,14 @@ Prior to the first stable release we neither have formal release processes nor d
To create a new pre-release: To create a new pre-release:
1. Merge a release PR which updates the `-dev.X` versions in the workspace `Cargo.toml` and updates the `CHANGELOG.md` accordingly 1. Merge a release PR which updates the `-dev.X` versions in the workspace `Cargo.toml` and updates the `CHANGELOG.md` accordingly. The release workflow will attempt to build and publish a new release whenever the latest git tag does not match the cargo package version.
2. Push a release tag to `main` 2. Wait for the `Release` workflow to finish. If the workflow fails after the `build-linux-all` step, check if a tag has been created and delete it before restarting or pushing updates. Note: It's more convenient to debug the release workflow in a fork (the fork has to be under the `paritytech` org to access `parity-large` runners).
3. Create a __pre-release__ from the tag and manually upload the `resolc` binary from docker image 3. Check draft release on [Releases page](https://github.com/paritytech/revive/releases) and publish (should contain `resolc.js`, `resolc.wasm`, `resolc-web.js`, and `resolc-static-linux` release assets)
4. Manually upload `resolc.js` and `resolc.wasm` from the `build-revive-wasm` action artifacts. 4. Update the [contract-docs](https://github.com/paritytech/contract-docs/) accordingly
5. Update the [contract-docs](https://github.com/paritytech/contract-docs/) accordingly
# LLVM release
To create a new LLVM release, run "Release LLVM" workflow. Use current LLVM version as parameter, e.g. `18.1.8`.
Version suffix will be resolved automatically.
The workflows will create new GitHub release, and upload LLVM binaries.
Next release of resolc will use newly created binaries.
-6
View File
@@ -12,12 +12,6 @@ pub static EXTENSION_ABI: &str = "abi";
/// The Yul IR file extension. /// The Yul IR file extension.
pub static EXTENSION_YUL: &str = "yul"; pub static EXTENSION_YUL: &str = "yul";
/// The EVM legacy assembly IR file extension.
pub static EXTENSION_EVMLA: &str = "evmla";
/// The Ethereal IR file extension.
pub static EXTENSION_ETHIR: &str = "ethir";
/// The EVM file extension. /// The EVM file extension.
pub static EXTENSION_EVM: &str = "evm"; pub static EXTENSION_EVM: &str = "evm";
+9 -2
View File
@@ -16,9 +16,16 @@
"shanghaiTime": 0, "shanghaiTime": 0,
"cancunTime": 0, "cancunTime": 0,
"terminalTotalDifficulty": 0, "terminalTotalDifficulty": 0,
"terminalTotalDifficultyPassed": true "terminalTotalDifficultyPassed": true,
"blobSchedule": {
"cancun": {
"target": 3,
"max": 6,
"baseFeeUpdateFraction": 3338477
}
}
}, },
"coinbase": "0x0000000000000000000000000000000000000000", "coinbase": "0xffffffffffffffffffffffffffffffffffffffff",
"difficulty": "0x20000", "difficulty": "0x20000",
"extraData": "", "extraData": "",
"gasLimit": "0xffffffff", "gasLimit": "0xffffffff",
+1 -1
View File
@@ -413,7 +413,7 @@ impl Evm {
let stderr = str::from_utf8(output.stderr.as_slice()) let stderr = str::from_utf8(output.stderr.as_slice())
.unwrap_or_else(|err| panic!("{EXECUTABLE_NAME} stderr failed to parse: {err}")); .unwrap_or_else(|err| panic!("{EXECUTABLE_NAME} stderr failed to parse: {err}"));
let mut log: EvmLog = stdout.into(); let mut log: EvmLog = format!("{stdout}{stderr}").as_str().into();
log.stderr = stderr.into(); log.stderr = stderr.into();
if self.bench { if self.bench {
log.parse_gas_used_from_bench(); log.parse_gas_used_from_bench();
+81
View File
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Upload": {
"code": {
"Solidity": {
"contract": "BalanceReceiver"
}
}
}
},
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "Balance"
}
},
"value": 24
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "1c8d16b30000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "6ada15d90000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
}
}
]
}
*/
contract BalanceReceiver {
constructor() payable {}
fallback() external payable {}
}
contract Balance {
constructor() payable {
// 0 to EOA
transfer_to(payable(address(0xdeadbeef)), 0);
send_to(payable(address(0xdeadbeef)), 0);
// 1 to EOA
transfer_to(payable(address(0xcafebabe)), 1);
send_to(payable(address(0xcafebabe)), 1);
BalanceReceiver balanceReceiver = new BalanceReceiver();
// 0 to contract
transfer_to(payable(address(balanceReceiver)), 0);
send_to(payable(address(balanceReceiver)), 0);
// 1 to contract
transfer_to(payable(address(balanceReceiver)), 1);
send_to(payable(address(balanceReceiver)), 1);
}
function transfer_to(address payable _dest, uint _amount) public payable {
_dest.transfer(_amount);
}
function send_to(address payable _dest, uint _amount) public payable {
require(_dest.send(_amount));
}
}
+27
View File
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "Coinbase"
}
}
}
}
]
}
*/
contract Coinbase {
constructor() payable {
address coinbase = address(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF);
assert(block.coinbase == coinbase);
}
}
+50
View File
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Upload": {
"code": {
"Solidity": {
"contract": "CreateA"
}
}
}
},
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "CreateB"
}
},
"value": 100000
}
}
]
}
*/
contract CreateA {
constructor() payable {}
}
contract CreateB {
constructor() payable {
bytes32 salt = hex"ff";
try new CreateA{salt: salt}() returns (CreateA) {} catch {
revert("the first instantiation should succeed");
}
try new CreateA{salt: salt}() returns (CreateA) {} catch {
return;
}
revert("the second instantiation should have failed");
}
}
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "FunctionPointer"
}
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "26121ff0"
}
}
]
}
*/
contract FunctionPointer {
bool public flag = false;
function f0() public {
flag = true;
}
function f() public returns (bool) {
function() internal x = f0;
x();
return flag;
}
}
+40
View File
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/* runner.json
{
"differential": true,
"actions": [
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "MLoad"
}
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "e2179b8e"
}
}
]
}
*/
contract MLoad {
constructor() payable {
assert(g() == 0);
}
function g() public payable returns (uint m) {
assembly {
m := mload(0)
}
}
}
+75
View File
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": false,
"actions": [
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "Send"
}
},
"value": 211
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "1c8d16b30000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
}
},
{
"VerifyCall": {
"success": true
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000001"
}
},
{
"VerifyCall": {
"success": false
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000000"
}
},
{
"VerifyCall": {
"success": false
}
}
]
}
*/
contract Send {
constructor() payable {}
function transfer_self(uint _amount) public payable {
transfer_to(payable(address(this)), _amount);
}
function transfer_to(address payable _dest, uint _amount) public payable {
if (_dest.send(_amount)) {}
}
fallback() external {}
receive() external payable {}
}
+32 -11
View File
@@ -3,7 +3,7 @@ pragma solidity ^0.8;
/* runner.json /* runner.json
{ {
"differential": true, "differential": false,
"actions": [ "actions": [
{ {
"Instantiate": { "Instantiate": {
@@ -12,7 +12,7 @@ pragma solidity ^0.8;
"contract": "Transfer" "contract": "Transfer"
} }
}, },
"value": 11 "value": 211
} }
}, },
{ {
@@ -23,12 +23,35 @@ pragma solidity ^0.8;
"data": "1c8d16b30000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a" "data": "1c8d16b30000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
} }
}, },
{
"VerifyCall": {
"success": true
}
},
{ {
"Call": { "Call": {
"dest": { "dest": {
"Instantiated": 0 "Instantiated": 0
}, },
"data": "fb9e8d0500000000000000000000000003030303030303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000001" "data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000001"
}
},
{
"VerifyCall": {
"success": false
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000000"
}
},
{
"VerifyCall": {
"success": false
} }
} }
] ]
@@ -36,19 +59,17 @@ pragma solidity ^0.8;
*/ */
contract Transfer { contract Transfer {
constructor() payable { constructor() payable {}
transfer_self(msg.value);
}
function address_self() internal view returns (address payable) {
return payable(address(this));
}
function transfer_self(uint _amount) public payable { function transfer_self(uint _amount) public payable {
transfer_to(address_self(), _amount); transfer_to(payable(address(this)), _amount);
} }
function transfer_to(address payable _dest, uint _amount) public payable { function transfer_to(address payable _dest, uint _amount) public payable {
_dest.transfer(_amount); _dest.transfer(_amount);
} }
fallback() external {}
receive() external payable {}
} }
+14
View File
@@ -156,6 +156,20 @@ case!("DivisionArithmetics.sol", DivisionArithmetics, sdivCall, division_arithme
case!("DivisionArithmetics.sol", DivisionArithmetics, modCall, division_arithmetics_mod, n: U256, d: U256); case!("DivisionArithmetics.sol", DivisionArithmetics, modCall, division_arithmetics_mod, n: U256, d: U256);
case!("DivisionArithmetics.sol", DivisionArithmetics, smodCall, division_arithmetics_smod, n: I256, d: I256); case!("DivisionArithmetics.sol", DivisionArithmetics, smodCall, division_arithmetics_smod, n: I256, d: I256);
sol!(
contract Send {
function transfer_self(uint _amount) public payable;
}
);
case!("Send.sol", Send, transfer_selfCall, send_self, amount: U256);
sol!(
contract Transfer {
function transfer_self(uint _amount) public payable;
}
);
case!("Transfer.sol", Transfer, transfer_selfCall, transfer_self, amount: U256);
sol!( sol!(
contract MStore8 { contract MStore8 {
function mStore8(uint value) public pure returns (uint256 word); function mStore8(uint value) public pure returns (uint256 word);
+48 -30
View File
@@ -37,10 +37,10 @@ test_spec!(events, "Events", "Events.sol");
test_spec!(storage, "Storage", "Storage.sol"); test_spec!(storage, "Storage", "Storage.sol");
test_spec!(mstore8, "MStore8", "MStore8.sol"); test_spec!(mstore8, "MStore8", "MStore8.sol");
test_spec!(address, "Context", "Context.sol"); test_spec!(address, "Context", "Context.sol");
test_spec!(balance, "Value", "Value.sol"); test_spec!(value, "Value", "Value.sol");
test_spec!(create, "CreateB", "Create.sol"); test_spec!(create, "CreateB", "Create.sol");
test_spec!(call, "Caller", "Call.sol"); test_spec!(call, "Caller", "Call.sol");
test_spec!(transfer, "Transfer", "Transfer.sol"); test_spec!(balance, "Balance", "Balance.sol");
test_spec!(return_data_oob, "ReturnDataOob", "ReturnDataOob.sol"); test_spec!(return_data_oob, "ReturnDataOob", "ReturnDataOob.sol");
test_spec!(immutables, "Immutables", "Immutables.sol"); test_spec!(immutables, "Immutables", "Immutables.sol");
test_spec!(transaction, "Transaction", "Transaction.sol"); test_spec!(transaction, "Transaction", "Transaction.sol");
@@ -50,6 +50,12 @@ test_spec!(gas_price, "GasPrice", "GasPrice.sol");
test_spec!(gas_left, "GasLeft", "GasLeft.sol"); test_spec!(gas_left, "GasLeft", "GasLeft.sol");
test_spec!(gas_limit, "GasLimit", "GasLimit.sol"); test_spec!(gas_limit, "GasLimit", "GasLimit.sol");
test_spec!(base_fee, "BaseFee", "BaseFee.sol"); test_spec!(base_fee, "BaseFee", "BaseFee.sol");
test_spec!(coinbase, "Coinbase", "Coinbase.sol");
test_spec!(create2, "CreateB", "Create2.sol");
test_spec!(transfer, "Transfer", "Transfer.sol");
test_spec!(send, "Send", "Send.sol");
test_spec!(function_pointer, "FunctionPointer", "FunctionPointer.sol");
test_spec!(mload, "MLoad", "MLoad.sol");
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> { fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
vec![Instantiate { vec![Instantiate {
@@ -61,7 +67,6 @@ fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
path: Some(path.into()), path: Some(path.into()),
contract: contract.to_string(), contract: contract.to_string(),
solc_optimizer: None, solc_optimizer: None,
pipeline: None,
}, },
data: vec![], data: vec![],
salt: OptionalHex::default(), salt: OptionalHex::default(),
@@ -354,7 +359,6 @@ fn ext_code_size() {
path: Some("contracts/Baseline.sol".into()), path: Some("contracts/Baseline.sol".into()),
contract: "Baseline".to_string(), contract: "Baseline".to_string(),
solc_optimizer: None, solc_optimizer: None,
pipeline: None,
}, },
data: vec![], data: vec![],
salt: OptionalHex::from([0; 32]), salt: OptionalHex::from([0; 32]),
@@ -435,32 +439,46 @@ fn ext_code_size() {
.run(); .run();
} }
/* #[test]
// These test were implement for the mock-runtime and need to be ported yet. #[should_panic(expected = "ReentranceDenied")]
fn send_denies_reentrancy() {
let value = 1000;
Specs {
actions: vec![
instantiate("contracts/Send.sol", "Send").remove(0),
Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::send_self(U256::from(value)).calldata,
},
],
differential: false,
..Default::default()
}
.run();
}
#[test] #[test]
fn create2_failure() { #[should_panic(expected = "ReentranceDenied")]
let mut state = State::default(); fn transfer_denies_reentrancy() {
let contract_a = Contract::create_a(); let value = 1000;
state.upload_code(&contract_a.pvm_runtime); Specs {
actions: vec![
let contract = Contract::create_b(); instantiate("contracts/Transfer.sol", "Transfer").remove(0),
let (state, output) = state Call {
.transaction() origin: TestAddress::Alice,
.with_default_account(&contract.pvm_runtime) dest: TestAddress::Instantiated(0),
.calldata(contract.calldata.clone()) value,
.call(); gas_limit: None,
storage_deposit_limit: None,
assert_eq!(output.flags, ReturnFlags::Success); data: Contract::transfer_self(U256::from(value)).calldata,
},
// The address already exists, which should cause the contract to revert ],
differential: false,
let (_, output) = state ..Default::default()
.transaction() }
.with_default_account(&contract.pvm_runtime) .run();
.calldata(contract.calldata)
.call();
assert_eq!(output.flags, ReturnFlags::Revert);
} }
*/
+3 -11
View File
@@ -40,17 +40,9 @@ pub const SHARED_BUILD_OPTS_NOT_MUSL: [&str; 4] = [
/// The shared build options to treat warnings as errors. /// The shared build options to treat warnings as errors.
/// ///
/// Disabled on Windows due to the following upstream issue with MSYS2 with mingw-w64: /// Disabled because it makes the build very brittle.
/// ProgramTest.cpp:23:15: error: '__p__environ' redeclared without 'dllimport' attribute pub fn shared_build_opts_werror(_target_env: TargetEnv) -> Vec<String> {
pub fn shared_build_opts_werror(target_env: TargetEnv) -> Vec<String> { vec!["-DLLVM_ENABLE_WERROR='Off'".to_string()]
vec![format!(
"-DLLVM_ENABLE_WERROR='{}'",
if cfg!(target_os = "windows") || target_env == TargetEnv::Emscripten {
"Off"
} else {
"On"
},
)]
} }
/// The build options to set the default target. /// The build options to set the default target.
@@ -141,7 +141,7 @@ fn build_target(
Command::new("emcmake") Command::new("emcmake")
.env("EMCC_DEBUG", "2") .env("EMCC_DEBUG", "2")
.env("CXXFLAGS", "-Dwait4=__syscall_wait4") .env("CXXFLAGS", "-Dwait4=__syscall_wait4")
.env("LDFLAGS", "-lnodefs.js -s NO_INVOKE_RUN -s EXIT_RUNTIME -s INITIAL_MEMORY=64MB -s ALLOW_MEMORY_GROWTH -s EXPORTED_RUNTIME_METHODS=FS,callMain,NODEFS -s MODULARIZE -s EXPORT_ES6 -s WASM_BIGINT") .env("LDFLAGS", "-lnodefs.js -s NO_INVOKE_RUN=1 -s EXIT_RUNTIME=1 -s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_RUNTIME_METHODS=FS,callMain,NODEFS -s MODULARIZE=1 -s WASM_BIGINT=1 -s ALLOW_TABLE_GROWTH=1 -s NODEJS_CATCH_EXIT=0 -sDYNAMIC_EXECUTION=0")
.arg("cmake") .arg("cmake")
.args([ .args([
"-S", "-S",
-1
View File
@@ -21,7 +21,6 @@ serde = { workspace = true, features = ["derive"] }
num = { workspace = true } num = { workspace = true }
hex = { workspace = true } hex = { workspace = true }
sha3 = { workspace = true } sha3 = { workspace = true }
md5 = { workspace = true }
inkwell = { workspace = true } inkwell = { workspace = true }
polkavm-disassembler = { workspace = true } polkavm-disassembler = { workspace = true }
polkavm-common = { workspace = true } polkavm-common = { workspace = true }
@@ -1,16 +1,11 @@
//! The debug IR type. //! The debug IR type.
/// The debug IR type. /// The debug IR type.
#[allow(non_camel_case_types)]
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IRType { pub enum IRType {
/// Whether to dump the Yul code. /// Whether to dump the Yul code.
Yul, Yul,
/// Whether to dump the EVM legacy assembly code.
EVMLA,
/// Whether to dump the Ethereal IR code.
EthIR,
/// Whether to dump the LLVM IR code. /// Whether to dump the LLVM IR code.
LLVM, LLVM,
/// Whether to dump the assembly code. /// Whether to dump the assembly code.
@@ -27,8 +22,6 @@ impl IRType {
pub fn file_extension(&self) -> &'static str { pub fn file_extension(&self) -> &'static str {
match self { match self {
Self::Yul => revive_common::EXTENSION_YUL, Self::Yul => revive_common::EXTENSION_YUL,
Self::EthIR => revive_common::EXTENSION_ETHIR,
Self::EVMLA => revive_common::EXTENSION_EVMLA,
Self::LLVM => revive_common::EXTENSION_LLVM_SOURCE, Self::LLVM => revive_common::EXTENSION_LLVM_SOURCE,
Self::Assembly => revive_common::EXTENSION_POLKAVM_ASSEMBLY, Self::Assembly => revive_common::EXTENSION_POLKAVM_ASSEMBLY,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@@ -39,30 +39,6 @@ impl DebugConfig {
Ok(()) Ok(())
} }
/// Dumps the EVM legacy assembly IR.
pub fn dump_evmla(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
if let Some(output_directory) = self.output_directory.as_ref() {
let mut file_path = output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::EVMLA);
file_path.push(full_file_name);
std::fs::write(file_path, code)?;
}
Ok(())
}
/// Dumps the Ethereal IR.
pub fn dump_ethir(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
if let Some(output_directory) = self.output_directory.as_ref() {
let mut file_path = output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::EthIR);
file_path.push(full_file_name);
std::fs::write(file_path, code)?;
}
Ok(())
}
/// Dumps the unoptimized LLVM IR. /// Dumps the unoptimized LLVM IR.
pub fn dump_llvm_ir_unoptimized( pub fn dump_llvm_ir_unoptimized(
&self, &self,
-5
View File
@@ -17,12 +17,7 @@ pub use self::polkavm::context::attribute::Attribute as PolkaVMAttribute;
pub use self::polkavm::context::build::Build as PolkaVMBuild; pub use self::polkavm::context::build::Build as PolkaVMBuild;
pub use self::polkavm::context::code_type::CodeType as PolkaVMCodeType; pub use self::polkavm::context::code_type::CodeType as PolkaVMCodeType;
pub use self::polkavm::context::debug_info::DebugInfo; pub use self::polkavm::context::debug_info::DebugInfo;
pub use self::polkavm::context::evmla_data::EVMLAData as PolkaVMContextEVMLAData;
pub use self::polkavm::context::function::block::evmla_data::key::Key as PolkaVMFunctionBlockKey;
pub use self::polkavm::context::function::block::evmla_data::EVMLAData as PolkaVMFunctionBlockEVMLAData;
pub use self::polkavm::context::function::block::Block as PolkaVMFunctionBlock;
pub use self::polkavm::context::function::declaration::Declaration as PolkaVMFunctionDeclaration; pub use self::polkavm::context::function::declaration::Declaration as PolkaVMFunctionDeclaration;
pub use self::polkavm::context::function::evmla_data::EVMLAData as PolkaVMFunctionEVMLAData;
pub use self::polkavm::context::function::intrinsics::Intrinsics as PolkaVMIntrinsicFunction; pub use self::polkavm::context::function::intrinsics::Intrinsics as PolkaVMIntrinsicFunction;
pub use self::polkavm::context::function::llvm_runtime::LLVMRuntime as PolkaVMLLVMRuntime; pub use self::polkavm::context::function::llvm_runtime::LLVMRuntime as PolkaVMLLVMRuntime;
pub use self::polkavm::context::function::r#return::Return as PolkaVMFunctionReturn; pub use self::polkavm::context::function::r#return::Return as PolkaVMFunctionReturn;
@@ -6,15 +6,9 @@ pub const LLVM_VERSION: semver::Version = semver::Version::new(18, 1, 4);
/// The pointer width sized type. /// The pointer width sized type.
pub static XLEN: usize = revive_common::BIT_LENGTH_X32; pub static XLEN: usize = revive_common::BIT_LENGTH_X32;
/// The heap memory pointer pointer global variable name.
pub static GLOBAL_HEAP_MEMORY_POINTER: &str = "memory_pointer";
/// The calldata size global variable name. /// The calldata size global variable name.
pub static GLOBAL_CALLDATA_SIZE: &str = "calldatasize"; pub static GLOBAL_CALLDATA_SIZE: &str = "calldatasize";
/// The call flags global variable name.
pub static GLOBAL_CALL_FLAGS: &str = "call_flags";
/// The deployer call header size that consists of: /// The deployer call header size that consists of:
/// - bytecode hash (32 bytes) /// - bytecode hash (32 bytes)
pub const DEPLOYER_CALL_HEADER_SIZE: usize = revive_common::BYTE_LENGTH_WORD; pub const DEPLOYER_CALL_HEADER_SIZE: usize = revive_common::BYTE_LENGTH_WORD;
@@ -1,27 +0,0 @@
//! The LLVM IR generator EVM legacy assembly data.
use crate::polkavm::context::argument::Argument;
/// The LLVM IR generator EVM legacy assembly data.
/// Describes some data that is only relevant to the EVM legacy assembly.
#[derive(Debug, Clone)]
pub struct EVMLAData<'ctx> {
/// The Solidity compiler version.
/// Some instruction behave differenly depending on the version.
pub version: semver::Version,
/// The static stack allocated for the current function.
pub stack: Vec<Argument<'ctx>>,
}
impl EVMLAData<'_> {
/// The default stack size.
pub const DEFAULT_STACK_SIZE: usize = 64;
/// A shortcut constructor.
pub fn new(version: semver::Version) -> Self {
Self {
version,
stack: Vec::with_capacity(Self::DEFAULT_STACK_SIZE),
}
}
}
@@ -1,34 +0,0 @@
//! The LLVM IR generator function block key.
use crate::polkavm::context::code_type::CodeType;
/// The LLVM IR generator function block key.
/// Is only relevant to the EVM legacy assembly.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Key {
/// The block code type.
pub code_type: CodeType,
/// The block tag.
pub tag: num::BigUint,
}
impl Key {
/// A shortcut constructor.
pub fn new(code_type: CodeType, tag: num::BigUint) -> Self {
Self { code_type, tag }
}
}
impl std::fmt::Display for Key {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}_{}",
match self.code_type {
CodeType::Deploy => "dt",
CodeType::Runtime => "rt",
},
self.tag
)
}
}
@@ -1,18 +0,0 @@
//! The LLVM function block EVM legacy assembly data.
pub mod key;
/// The LLVM function block EVM legacy assembly data.
/// Describes some data that is only relevant to the EVM legacy assembly.
#[derive(Debug, Clone)]
pub struct EVMLAData {
/// The initial hashes of the allowed stack states.
pub stack_hashes: Vec<md5::Digest>,
}
impl EVMLAData {
/// A shortcut constructor.
pub fn new(stack_hashes: Vec<md5::Digest>) -> Self {
Self { stack_hashes }
}
}
@@ -1,52 +0,0 @@
//! The LLVM IR generator function block.
pub mod evmla_data;
use self::evmla_data::EVMLAData;
/// The LLVM IR generator function block.
#[derive(Debug, Clone)]
pub struct Block<'ctx> {
/// The inner block.
inner: inkwell::basic_block::BasicBlock<'ctx>,
/// The EVM legacy assembly compiler data.
evmla_data: Option<EVMLAData>,
}
impl<'ctx> Block<'ctx> {
/// A shortcut constructor.
pub fn new(inner: inkwell::basic_block::BasicBlock<'ctx>) -> Self {
Self {
inner,
evmla_data: None,
}
}
/// Sets the EVM legacy assembly data.
pub fn set_evmla_data(&mut self, data: EVMLAData) {
self.evmla_data = Some(data);
}
/// The LLVM object reference.
pub fn inner(&self) -> inkwell::basic_block::BasicBlock<'ctx> {
self.inner
}
/// Returns the EVM data reference.
/// # Panics
/// If the EVM data has not been initialized.
pub fn evm(&self) -> &EVMLAData {
self.evmla_data
.as_ref()
.expect("The EVM data must have been initialized")
}
/// Returns the EVM data mutable reference.
/// # Panics
/// If the EVM data has not been initialized.
pub fn evm_mut(&mut self) -> &mut EVMLAData {
self.evmla_data
.as_mut()
.expect("The EVM data must have been initialized")
}
}
@@ -1,74 +0,0 @@
//! The LLVM function EVM legacy assembly data.
use std::collections::BTreeMap;
use crate::polkavm::context::function::block::evmla_data::key::Key as BlockKey;
use crate::polkavm::context::function::block::Block;
/// The LLVM function EVM legacy assembly data.
/// Describes some data that is only relevant to the EVM legacy assembly.
#[derive(Debug)]
pub struct EVMLAData<'ctx> {
/// The ordinary blocks with numeric tags.
/// Is only used by the Solidity EVM compiler.
pub blocks: BTreeMap<BlockKey, Vec<Block<'ctx>>>,
/// The function stack size.
pub stack_size: usize,
}
impl<'ctx> EVMLAData<'ctx> {
/// A shortcut constructor.
pub fn new(stack_size: usize) -> Self {
Self {
blocks: BTreeMap::new(),
stack_size,
}
}
/// Inserts a function block.
pub fn insert_block(&mut self, key: BlockKey, block: Block<'ctx>) {
if let Some(blocks) = self.blocks.get_mut(&key) {
blocks.push(block);
} else {
self.blocks.insert(key, vec![block]);
}
}
/// Returns the block with the specified tag and initial stack pattern.
/// If there is only one block, it is returned unconditionally.
pub fn find_block(
&self,
key: &BlockKey,
stack_hash: &md5::Digest,
) -> anyhow::Result<Block<'ctx>> {
if self
.blocks
.get(key)
.ok_or_else(|| anyhow::anyhow!("Undeclared function block {}", key))?
.len()
== 1
{
return self
.blocks
.get(key)
.ok_or_else(|| anyhow::anyhow!("Undeclared function block {}", key))?
.first()
.cloned()
.ok_or_else(|| anyhow::anyhow!("Undeclared function block {}", key));
}
self.blocks
.get(key)
.ok_or_else(|| anyhow::anyhow!("Undeclared function block {}", key))?
.iter()
.find(|block| {
block
.evm()
.stack_hashes
.iter()
.any(|hash| hash == stack_hash)
})
.cloned()
.ok_or_else(|| anyhow::anyhow!("Undeclared function block {}", key))
}
}
@@ -1,8 +1,6 @@
//! The LLVM IR generator function. //! The LLVM IR generator function.
pub mod block;
pub mod declaration; pub mod declaration;
pub mod evmla_data;
pub mod intrinsics; pub mod intrinsics;
pub mod llvm_runtime; pub mod llvm_runtime;
pub mod r#return; pub mod r#return;
@@ -19,7 +17,6 @@ use crate::polkavm::context::attribute::Attribute;
use crate::polkavm::context::pointer::Pointer; use crate::polkavm::context::pointer::Pointer;
use self::declaration::Declaration; use self::declaration::Declaration;
use self::evmla_data::EVMLAData;
use self::r#return::Return; use self::r#return::Return;
use self::yul_data::YulData; use self::yul_data::YulData;
@@ -45,8 +42,6 @@ pub struct Function<'ctx> {
/// The Yul compiler data. /// The Yul compiler data.
yul_data: Option<YulData>, yul_data: Option<YulData>,
/// The EVM legacy assembly compiler data.
evmla_data: Option<EVMLAData<'ctx>>,
} }
impl<'ctx> Function<'ctx> { impl<'ctx> Function<'ctx> {
@@ -72,7 +67,6 @@ impl<'ctx> Function<'ctx> {
return_block, return_block,
yul_data: None, yul_data: None,
evmla_data: None,
} }
} }
@@ -218,22 +212,6 @@ impl<'ctx> Function<'ctx> {
} }
} }
/// Sets the exception handler attributes.
pub fn set_exception_handler_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
) {
Self::set_attributes(llvm, declaration, vec![Attribute::NoInline], false);
}
/// Sets the CXA-throw attributes.
pub fn set_cxa_throw_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
) {
Self::set_attributes(llvm, declaration, vec![Attribute::NoProfile], false);
}
/// Sets the pure function attributes. /// Sets the pure function attributes.
pub fn set_pure_function_attributes( pub fn set_pure_function_attributes(
llvm: &'ctx inkwell::context::Context, llvm: &'ctx inkwell::context::Context,
@@ -300,29 +278,6 @@ impl<'ctx> Function<'ctx> {
self.return_block self.return_block
} }
/// Sets the EVM legacy assembly data.
pub fn set_evmla_data(&mut self, data: EVMLAData<'ctx>) {
self.evmla_data = Some(data);
}
/// Returns the EVM legacy assembly data reference.
/// # Panics
/// If the EVM data has not been initialized.
pub fn evmla(&self) -> &EVMLAData<'ctx> {
self.evmla_data
.as_ref()
.expect("The EVM data must have been initialized")
}
/// Returns the EVM legacy assembly data mutable reference.
/// # Panics
/// If the EVM data has not been initialized.
pub fn evmla_mut(&mut self) -> &mut EVMLAData<'ctx> {
self.evmla_data
.as_mut()
.expect("The EVM data must have been initialized")
}
/// Sets the Yul data. /// Sets the Yul data.
pub fn set_yul_data(&mut self, data: YulData) { pub fn set_yul_data(&mut self, data: YulData) {
self.yul_data = Some(data); self.yul_data = Some(data);
@@ -24,22 +24,6 @@ impl Entry {
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
context.set_global(
crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER,
context.llvm().ptr_type(AddressSpace::Heap.into()),
AddressSpace::Stack,
context.xlen_type().get_undef(),
);
context.build_store(
context
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
.into(),
context.build_sbrk(
context.xlen_type().const_zero(),
context.xlen_type().const_zero(),
)?,
)?;
context.set_global( context.set_global(
crate::polkavm::GLOBAL_CALLDATA_SIZE, crate::polkavm::GLOBAL_CALLDATA_SIZE,
context.xlen_type(), context.xlen_type(),
@@ -47,13 +31,6 @@ impl Entry {
context.xlen_type().get_undef(), context.xlen_type().get_undef(),
); );
context.set_global(
crate::polkavm::GLOBAL_CALL_FLAGS,
context.word_type(),
AddressSpace::Stack,
context.word_const(0),
);
Ok(()) Ok(())
} }
@@ -70,6 +47,11 @@ impl Entry {
.build_runtime_call(revive_runtime_api::polkavm_imports::CALL_DATA_SIZE, &[]) .build_runtime_call(revive_runtime_api::polkavm_imports::CALL_DATA_SIZE, &[])
.expect("the call_data_size syscall method should return a value") .expect("the call_data_size syscall method should return a value")
.into_int_value(); .into_int_value();
let call_data_size_value = context.builder().build_int_truncate(
call_data_size_value,
context.xlen_type(),
"call_data_size_truncated",
)?;
context context
.builder() .builder()
.build_store(call_data_size_pointer, call_data_size_value)?; .build_store(call_data_size_pointer, call_data_size_value)?;
@@ -90,13 +72,6 @@ impl Entry {
.borrow() .borrow()
.get_nth_param(Self::ARGUMENT_INDEX_CALL_FLAGS); .get_nth_param(Self::ARGUMENT_INDEX_CALL_FLAGS);
context.set_global(
crate::polkavm::GLOBAL_CALL_FLAGS,
is_deploy.get_type(),
AddressSpace::Stack,
is_deploy.into_int_value(),
);
let deploy_code_call_block = context.append_basic_block("deploy_code_call_block"); let deploy_code_call_block = context.append_basic_block("deploy_code_call_block");
let runtime_code_call_block = context.append_basic_block("runtime_code_call_block"); let runtime_code_call_block = context.append_basic_block("runtime_code_call_block");
+14 -41
View File
@@ -6,7 +6,6 @@ pub mod attribute;
pub mod build; pub mod build;
pub mod code_type; pub mod code_type;
pub mod debug_info; pub mod debug_info;
pub mod evmla_data;
pub mod function; pub mod function;
pub mod global; pub mod global;
pub mod r#loop; pub mod r#loop;
@@ -38,7 +37,6 @@ use self::attribute::Attribute;
use self::build::Build; use self::build::Build;
use self::code_type::CodeType; use self::code_type::CodeType;
use self::debug_info::DebugInfo; use self::debug_info::DebugInfo;
use self::evmla_data::EVMLAData;
use self::function::declaration::Declaration as FunctionDeclaration; use self::function::declaration::Declaration as FunctionDeclaration;
use self::function::intrinsics::Intrinsics; use self::function::intrinsics::Intrinsics;
use self::function::llvm_runtime::LLVMRuntime; use self::function::llvm_runtime::LLVMRuntime;
@@ -95,8 +93,6 @@ where
solidity_data: Option<SolidityData>, solidity_data: Option<SolidityData>,
/// The Yul data. /// The Yul data.
yul_data: Option<YulData>, yul_data: Option<YulData>,
/// The EVM legacy assembly data.
evmla_data: Option<EVMLAData<'ctx>>,
} }
impl<'ctx, D> Context<'ctx, D> impl<'ctx, D> Context<'ctx, D>
@@ -257,7 +253,6 @@ where
solidity_data: None, solidity_data: None,
yul_data: None, yul_data: None,
evmla_data: None,
} }
} }
@@ -1214,17 +1209,17 @@ where
/// Build a call to PolkaVM `msize` for querying the linear memory size. /// Build a call to PolkaVM `msize` for querying the linear memory size.
pub fn build_msize(&self) -> anyhow::Result<inkwell::values::IntValue<'ctx>> { pub fn build_msize(&self) -> anyhow::Result<inkwell::values::IntValue<'ctx>> {
Ok(self let memory_size_pointer = self
.builder() .module()
.build_call( .get_global(revive_runtime_api::polkavm_imports::MEMORY_SIZE)
self.runtime_api_method(revive_runtime_api::polkavm_imports::MEMORY_SIZE), .expect("the memory size symbol should have been declared")
&[], .as_pointer_value();
"call_msize", let memory_size_value = self.builder().build_load(
)? self.xlen_type(),
.try_as_basic_value() memory_size_pointer,
.left() "memory_size_value",
.expect("sbrk returns an int") )?;
.into_int_value()) Ok(memory_size_value.into_int_value())
} }
/// Call PolkaVM `sbrk` for extending the heap by `offset` + `size`, /// Call PolkaVM `sbrk` for extending the heap by `offset` + `size`,
@@ -1270,8 +1265,9 @@ where
self.build_heap_alloc(offset, length)?; self.build_heap_alloc(offset, length)?;
let heap_start = self let heap_start = self
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)? .module()
.value .get_global(revive_runtime_api::polkavm_imports::MEMORY)
.expect("the memory symbol should have been declared")
.as_pointer_value(); .as_pointer_value();
Ok(self.build_gep( Ok(self.build_gep(
Pointer::new(self.byte_type(), AddressSpace::Stack, heap_start), Pointer::new(self.byte_type(), AddressSpace::Stack, heap_start),
@@ -1574,29 +1570,6 @@ where
.expect("The Yul data must have been initialized") .expect("The Yul data must have been initialized")
} }
/// Sets the EVM legacy assembly data.
pub fn set_evmla_data(&mut self, data: EVMLAData<'ctx>) {
self.evmla_data = Some(data);
}
/// Returns the EVM legacy assembly data reference.
/// # Panics
/// If the EVM data has not been initialized.
pub fn evmla(&self) -> &EVMLAData<'ctx> {
self.evmla_data
.as_ref()
.expect("The EVMLA data must have been initialized")
}
/// Returns the EVM legacy assembly data mutable reference.
/// # Panics
/// If the EVM data has not been initialized.
pub fn evmla_mut(&mut self) -> &mut EVMLAData<'ctx> {
self.evmla_data
.as_mut()
.expect("The EVMLA data must have been initialized")
}
/// Returns the current number of immutables values in the contract. /// Returns the current number of immutables values in the contract.
/// If the size is set manually, then it is returned. Otherwise, the number of elements in /// If the size is set manually, then it is returned. Otherwise, the number of elements in
/// the identifier-to-offset mapping tree is returned. /// the identifier-to-offset mapping tree is returned.
+95 -18
View File
@@ -8,6 +8,7 @@ use crate::polkavm::Dependency;
const STATIC_CALL_FLAG: u32 = 0b0001_0000; const STATIC_CALL_FLAG: u32 = 0b0001_0000;
const REENTRANT_CALL_FLAG: u32 = 0b0000_1000; const REENTRANT_CALL_FLAG: u32 = 0b0000_1000;
const SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD: u64 = 2300;
/// Translates a contract call. /// Translates a contract call.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@@ -37,26 +38,24 @@ where
let output_offset = context.safe_truncate_int_to_xlen(output_offset)?; let output_offset = context.safe_truncate_int_to_xlen(output_offset)?;
let output_length = context.safe_truncate_int_to_xlen(output_length)?; let output_length = context.safe_truncate_int_to_xlen(output_length)?;
// TODO: What to supply here? Is there a weight to gas?
let _gas = context
.builder()
.build_int_truncate(gas, context.integer_type(64), "gas")?;
let input_pointer = context.build_heap_gep(input_offset, input_length)?; let input_pointer = context.build_heap_gep(input_offset, input_length)?;
let output_pointer = context.build_heap_gep(output_offset, output_length)?; let output_pointer = context.build_heap_gep(output_offset, output_length)?;
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length"); let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
context.build_store(output_length_pointer, output_length)?; context.build_store(output_length_pointer, output_length)?;
let deposit_pointer = context.build_alloca_at_entry(context.word_type(), "deposit_pointer"); let (flags, deposit_limit_value) = if static_call {
context.build_store(deposit_pointer, context.word_type().const_all_ones())?; let flags = REENTRANT_CALL_FLAG | STATIC_CALL_FLAG;
(
let flags = if static_call { context.xlen_type().const_int(flags as u64, false),
REENTRANT_CALL_FLAG | STATIC_CALL_FLAG context.word_type().const_zero(),
)
} else { } else {
REENTRANT_CALL_FLAG call_reentrancy_heuristic(context, gas, input_length, output_length)?
}; };
let flags = context.xlen_type().const_int(flags as u64, false);
let deposit_pointer = context.build_alloca_at_entry(context.word_type(), "deposit_pointer");
context.build_store(deposit_pointer, deposit_limit_value)?;
let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg( let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(), context.builder(),
@@ -119,7 +118,7 @@ where
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn delegate_call<'ctx, D>( pub fn delegate_call<'ctx, D>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx, D>,
gas: inkwell::values::IntValue<'ctx>, _gas: inkwell::values::IntValue<'ctx>,
address: inkwell::values::IntValue<'ctx>, address: inkwell::values::IntValue<'ctx>,
input_offset: inkwell::values::IntValue<'ctx>, input_offset: inkwell::values::IntValue<'ctx>,
input_length: inkwell::values::IntValue<'ctx>, input_length: inkwell::values::IntValue<'ctx>,
@@ -137,11 +136,6 @@ where
let output_offset = context.safe_truncate_int_to_xlen(output_offset)?; let output_offset = context.safe_truncate_int_to_xlen(output_offset)?;
let output_length = context.safe_truncate_int_to_xlen(output_length)?; let output_length = context.safe_truncate_int_to_xlen(output_length)?;
// TODO: What to supply here? Is there a weight to gas?
let _gas = context
.builder()
.build_int_truncate(gas, context.integer_type(64), "gas")?;
let input_pointer = context.build_heap_gep(input_offset, input_length)?; let input_pointer = context.build_heap_gep(input_offset, input_length)?;
let output_pointer = context.build_heap_gep(output_offset, output_length)?; let output_pointer = context.build_heap_gep(output_offset, output_length)?;
@@ -221,3 +215,86 @@ where
.resolve_library(path.as_str())? .resolve_library(path.as_str())?
.as_basic_value_enum()) .as_basic_value_enum())
} }
/// The Solidity `address.transfer` and `address.send` call detection heuristic.
///
/// # Why
/// This heuristic is an additional security feature to guard against re-entrancy attacks
/// in case contract authors violate Solidity best practices and use `address.transfer` or
/// `address.send`.
/// While contract authors are supposed to never use `address.transfer` or `address.send`,
/// for a small cost we can be extra defensive about it.
///
/// # How
/// The gas stipend emitted by solc for `transfer` and `send` is not static, thus:
/// - Dynamically allow re-entrancy only for calls considered not transfer or send.
/// - Detected balance transfers will supply 0 deposit limit instead of `u256::MAX`.
///
/// Calls are considered transfer or send if:
/// - (Input length | Output lenght) == 0;
/// - Gas <= 2300;
///
/// # Returns
/// The call flags xlen `IntValue` and the deposit limit word `IntValue`.
fn call_reentrancy_heuristic<'ctx, D>(
context: &mut Context<'ctx, D>,
gas: inkwell::values::IntValue<'ctx>,
input_length: inkwell::values::IntValue<'ctx>,
output_length: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<(
inkwell::values::IntValue<'ctx>,
inkwell::values::IntValue<'ctx>,
)>
where
D: Dependency + Clone,
{
// Branch-free SSA implementation: First derive the heuristic boolean (int1) value.
let input_length_or_output_length =
context
.builder()
.build_or(input_length, output_length, "input_length_or_output_length")?;
let is_no_input_no_output = context.builder().build_int_compare(
inkwell::IntPredicate::EQ,
context.xlen_type().const_zero(),
input_length_or_output_length,
"is_no_input_no_output",
)?;
let gas_stipend = context
.word_type()
.const_int(SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD, false);
let is_gas_stipend_for_transfer_or_send = context.builder().build_int_compare(
inkwell::IntPredicate::ULE,
gas,
gas_stipend,
"is_gas_stipend_for_transfer_or_send",
)?;
let is_balance_transfer = context.builder().build_and(
is_no_input_no_output,
is_gas_stipend_for_transfer_or_send,
"is_balance_transfer",
)?;
let is_regular_call = context
.builder()
.build_not(is_balance_transfer, "is_balance_transfer_inverted")?;
// Call flag: Left shift the heuristic boolean value.
let is_regular_call_xlen = context.builder().build_int_z_extend(
is_regular_call,
context.xlen_type(),
"is_balance_transfer_xlen",
)?;
let call_flags = context.builder().build_left_shift(
is_regular_call_xlen,
context.xlen_type().const_int(3, false),
"flags",
)?;
// Deposit limit value: Sign-extended the heuristic boolean value.
let deposit_limit_value = context.builder().build_int_s_extend(
is_regular_call,
context.word_type(),
"deposit_limit_value",
)?;
Ok((call_flags, deposit_limit_value))
}
+10 -2
View File
@@ -122,12 +122,20 @@ where
/// Translates the `coinbase` instruction. /// Translates the `coinbase` instruction.
pub fn coinbase<'ctx, D>( pub fn coinbase<'ctx, D>(
_context: &mut Context<'ctx, D>, context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
todo!() let pointer = context.build_alloca_at_entry(
context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS),
"coinbase_output",
);
context.build_runtime_call(
revive_runtime_api::polkavm_imports::BLOCK_AUTHOR,
&[pointer.to_int(context).into()],
);
context.build_load_address(pointer)
} }
/// Translates the `basefee` instruction. /// Translates the `basefee` instruction.
-1
View File
@@ -59,7 +59,6 @@ pub fn build_assembly_text(
} }
/// Implemented by items which are translated into LLVM IR. /// Implemented by items which are translated into LLVM IR.
#[allow(clippy::upper_case_acronyms)]
pub trait WriteLLVM<D> pub trait WriteLLVM<D>
where where
D: Dependency + Clone, D: Dependency + Clone,
-3
View File
@@ -239,7 +239,6 @@ pub enum Code {
Solidity { Solidity {
path: Option<std::path::PathBuf>, path: Option<std::path::PathBuf>,
solc_optimizer: Option<bool>, solc_optimizer: Option<bool>,
pipeline: Option<revive_solidity::SolcPipeline>,
contract: String, contract: String,
}, },
/// Read the contract blob from disk /// Read the contract blob from disk
@@ -264,7 +263,6 @@ impl From<Code> for pallet_revive::Code {
path, path,
contract, contract,
solc_optimizer, solc_optimizer,
pipeline,
} => { } => {
let Some(path) = path else { let Some(path) = path else {
panic!("Solidity source of contract '{contract}' missing path"); panic!("Solidity source of contract '{contract}' missing path");
@@ -276,7 +274,6 @@ impl From<Code> for pallet_revive::Code {
&contract, &contract,
&source_code, &source_code,
solc_optimizer.unwrap_or(true), solc_optimizer.unwrap_or(true),
pipeline.unwrap_or(revive_solidity::SolcPipeline::Yul),
)) ))
} }
Code::Path(path) => pallet_revive::Code::Upload(std::fs::read(path).unwrap()), Code::Path(path) => pallet_revive::Code::Upload(std::fs::read(path).unwrap()),
+13 -5
View File
@@ -1,5 +1,4 @@
use frame_support::{runtime, weights::constants::WEIGHT_REF_TIME_PER_SECOND}; use frame_support::{runtime, traits::FindAuthor, weights::constants::WEIGHT_REF_TIME_PER_SECOND};
use pallet_revive::AccountId32Mapper; use pallet_revive::AccountId32Mapper;
use polkadot_sdk::*; use polkadot_sdk::*;
use polkadot_sdk::{ use polkadot_sdk::{
@@ -11,8 +10,6 @@ pub type Balance = u128;
pub type AccountId = pallet_revive::AccountId32Mapper<Runtime>; pub type AccountId = pallet_revive::AccountId32Mapper<Runtime>;
pub type Block = frame_system::mocking::MockBlock<Runtime>; pub type Block = frame_system::mocking::MockBlock<Runtime>;
pub type Hash = <Runtime as frame_system::Config>::Hash; pub type Hash = <Runtime as frame_system::Config>::Hash;
pub type EventRecord =
frame_system::EventRecord<<Runtime as frame_system::Config>::RuntimeEvent, Hash>;
#[runtime] #[runtime]
mod runtime { mod runtime {
@@ -26,7 +23,8 @@ mod runtime {
RuntimeHoldReason, RuntimeHoldReason,
RuntimeSlashReason, RuntimeSlashReason,
RuntimeLockId, RuntimeLockId,
RuntimeTask RuntimeTask,
RuntimeViewFunction
)] )]
pub struct Runtime; pub struct Runtime;
@@ -88,4 +86,14 @@ impl pallet_revive::Config for Runtime {
type InstantiateOrigin = EnsureSigned<AccountId32>; type InstantiateOrigin = EnsureSigned<AccountId32>;
type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
type ChainId = ConstU64<420_420_420>; type ChainId = ConstU64<420_420_420>;
type FindAuthor = Self;
}
impl FindAuthor<<Runtime as frame_system::Config>::AccountId> for Runtime {
fn find_author<'a, I>(_digests: I) -> Option<<Runtime as frame_system::Config>::AccountId>
where
I: 'a + IntoIterator<Item = (frame_support::ConsensusEngineId, &'a [u8])>,
{
Some([0xff; 32].into())
}
} }
-6
View File
@@ -282,17 +282,11 @@ impl Specs {
let Code::Solidity { let Code::Solidity {
path: Some(path), path: Some(path),
solc_optimizer, solc_optimizer,
pipeline,
contract, contract,
} = code } = code
else { else {
panic!("the differential runner requires Code::Solidity source"); panic!("the differential runner requires Code::Solidity source");
}; };
assert_ne!(
pipeline,
Some(revive_solidity::SolcPipeline::EVMLA),
"yul pipeline must be enabled in differential mode"
);
assert!( assert!(
salt.0.is_none(), salt.0.is_none(),
"salt is not supported in differential mode" "salt is not supported in differential mode"
+4 -6
View File
@@ -8,8 +8,8 @@
#define EVM_WORD_SIZE 32 #define EVM_WORD_SIZE 32
#define ALIGN(size) ((size + EVM_WORD_SIZE - 1) & ~(EVM_WORD_SIZE - 1)) #define ALIGN(size) ((size + EVM_WORD_SIZE - 1) & ~(EVM_WORD_SIZE - 1))
#define MAX_MEMORY_SIZE (64 * 1024) #define MAX_MEMORY_SIZE (64 * 1024)
static char __memory[MAX_MEMORY_SIZE]; char __memory[MAX_MEMORY_SIZE];
static uint32_t __memory_size = 0; uint32_t __memory_size = 0;
void * __sbrk_internal(uint32_t offset, uint32_t size) { void * __sbrk_internal(uint32_t offset, uint32_t size) {
if (offset >= MAX_MEMORY_SIZE || size > MAX_MEMORY_SIZE) { if (offset >= MAX_MEMORY_SIZE || size > MAX_MEMORY_SIZE) {
@@ -27,10 +27,6 @@ void * __sbrk_internal(uint32_t offset, uint32_t size) {
return (void *)&__memory[__memory_size]; return (void *)&__memory[__memory_size];
} }
uint32_t __msize() {
return __memory_size;
}
void * memset(void *b, int c, size_t len) { void * memset(void *b, int c, size_t len) {
uint8_t *dest = b; uint8_t *dest = b;
while (len-- > 0) *dest++ = c; while (len-- > 0) *dest++ = c;
@@ -72,6 +68,8 @@ POLKAVM_IMPORT(void, balance_of, uint32_t, uint32_t)
POLKAVM_IMPORT(void, base_fee, uint32_t) POLKAVM_IMPORT(void, base_fee, uint32_t)
POLKAVM_IMPORT(void, block_author, uint32_t)
POLKAVM_IMPORT(void, block_hash, uint32_t, uint32_t) POLKAVM_IMPORT(void, block_hash, uint32_t, uint32_t)
POLKAVM_IMPORT(void, block_number, uint32_t) POLKAVM_IMPORT(void, block_number, uint32_t)
+9 -11
View File
@@ -1,18 +1,14 @@
//! This crate vendors the [PolkaVM][0] C API and provides a LLVM module for interacting
//! with the `pallet-revive` runtime API.
//! At present, the revive pallet requires blobs to export `call` and `deploy`,
//! and offers a bunch of [runtime API methods][1]. The provided [module] implements
//! those exports and imports.
//! [0]: [https://crates.io/crates/polkavm]
//! [1]: [https://docs.rs/pallet-contracts/26.0.0/pallet_contracts/api_doc/index.html]
use inkwell::{context::Context, memory_buffer::MemoryBuffer, module::Module, support::LLVMString}; use inkwell::{context::Context, memory_buffer::MemoryBuffer, module::Module, support::LLVMString};
include!(concat!(env!("OUT_DIR"), "/polkavm_imports.rs")); include!(concat!(env!("OUT_DIR"), "/polkavm_imports.rs"));
pub static SBRK: &str = "__sbrk_internal"; /// The emulated EVM heap memory global symbol.
pub static MEMORY: &str = "__memory";
pub static MEMORY_SIZE: &str = "__msize"; /// The emulated EVM heap memory size global symbol.
pub static MEMORY_SIZE: &str = "__memory_size";
pub static SBRK: &str = "__sbrk_internal";
pub static ADDRESS: &str = "address"; pub static ADDRESS: &str = "address";
@@ -22,6 +18,8 @@ pub static BALANCE_OF: &str = "balance_of";
pub static BASE_FEE: &str = "base_fee"; pub static BASE_FEE: &str = "base_fee";
pub static BLOCK_AUTHOR: &str = "block_author";
pub static BLOCK_HASH: &str = "block_hash"; pub static BLOCK_HASH: &str = "block_hash";
pub static BLOCK_NUMBER: &str = "block_number"; pub static BLOCK_NUMBER: &str = "block_number";
@@ -82,11 +80,11 @@ pub static WEIGHT_TO_FEE: &str = "weight_to_fee";
/// Useful for configuring common attributes and linkage. /// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 34] = [ pub static IMPORTS: [&str; 34] = [
SBRK, SBRK,
MEMORY_SIZE,
ADDRESS, ADDRESS,
BALANCE, BALANCE,
BALANCE_OF, BALANCE_OF,
BASE_FEE, BASE_FEE,
BLOCK_AUTHOR,
BLOCK_HASH, BLOCK_HASH,
BLOCK_NUMBER, BLOCK_NUMBER,
CALL, CALL,
-1
View File
@@ -33,7 +33,6 @@ regex = { workspace = true }
hex = { workspace = true } hex = { workspace = true }
num = { workspace = true } num = { workspace = true }
sha3 = { workspace = true } sha3 = { workspace = true }
md5 = { workspace = true }
inkwell = { workspace = true } inkwell = { workspace = true }
revive-common = { workspace = true } revive-common = { workspace = true }
+2 -2
View File
@@ -64,7 +64,7 @@ impl Contract {
file_path.push(file_name); file_path.push(file_name);
if file_path.exists() && !overwrite { if file_path.exists() && !overwrite {
eprintln!( anyhow::bail!(
"Refusing to overwrite an existing file {file_path:?} (use --overwrite to force)." "Refusing to overwrite an existing file {file_path:?} (use --overwrite to force)."
); );
} else { } else {
@@ -87,7 +87,7 @@ impl Contract {
file_path.push(file_name); file_path.push(file_name);
if file_path.exists() && !overwrite { if file_path.exists() && !overwrite {
eprintln!( anyhow::bail!(
"Refusing to overwrite an existing file {file_path:?} (use --overwrite to force)." "Refusing to overwrite an existing file {file_path:?} (use --overwrite to force)."
); );
} else { } else {
-2
View File
@@ -1,7 +1,5 @@
//! Solidity to PolkaVM compiler constants. //! Solidity to PolkaVM compiler constants.
#![allow(dead_code)]
/// The default executable name. /// The default executable name.
pub static DEFAULT_EXECUTABLE_NAME: &str = "resolc"; pub static DEFAULT_EXECUTABLE_NAME: &str = "resolc";
@@ -1,67 +0,0 @@
//! The inner JSON legacy assembly code element.
use std::collections::HashSet;
use serde::Deserialize;
use serde::Serialize;
use crate::evmla::assembly::Assembly;
/// The inner JSON legacy assembly code element.
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(untagged)]
pub enum Data {
/// The assembly code wrapper.
Assembly(Assembly),
/// The hash.
Hash(String),
/// The full contract path after the factory dependencies replacing pass.
Path(String),
}
impl Data {
/// Returns the inner assembly reference if it is present.
pub fn get_assembly(&self) -> Option<&Assembly> {
match self {
Self::Assembly(ref assembly) => Some(assembly),
Self::Hash(_) => None,
Self::Path(_) => None,
}
}
/// Returns the inner assembly mutable reference if it is present.
pub fn get_assembly_mut(&mut self) -> Option<&mut Assembly> {
match self {
Self::Assembly(ref mut assembly) => Some(assembly),
Self::Hash(_) => None,
Self::Path(_) => None,
}
}
/// Get the list of missing deployable libraries.
pub fn get_missing_libraries(&self) -> HashSet<String> {
match self {
Self::Assembly(assembly) => assembly.get_missing_libraries(),
Self::Hash(_) => HashSet::new(),
Self::Path(_) => HashSet::new(),
}
}
/// Gets the contract `keccak256` hash.
pub fn keccak256(&self) -> String {
match self {
Self::Assembly(assembly) => assembly.keccak256(),
Self::Hash(hash) => panic!("Expected assembly, found hash `{hash}`"),
Self::Path(path) => panic!("Expected assembly, found path `{path}`"),
}
}
}
impl std::fmt::Display for Data {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Assembly(inner) => writeln!(f, "{inner}"),
Self::Hash(inner) => writeln!(f, "Hash `{inner}`"),
Self::Path(inner) => writeln!(f, "Path `{inner}`"),
}
}
}
@@ -1,79 +0,0 @@
//! Translates the CODECOPY use cases.
/// Translates the contract hash copying.
pub fn contract_hash<'ctx, D>(
context: &mut revive_llvm_context::PolkaVMContext<'ctx, D>,
offset: inkwell::values::IntValue<'ctx>,
value: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<()>
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
let offset = context.builder().build_int_add(
offset,
context
.word_const((revive_common::BYTE_LENGTH_X32 + revive_common::BYTE_LENGTH_WORD) as u64),
"datacopy_contract_hash_offset",
)?;
revive_llvm_context::polkavm_evm_memory::store(context, offset, value)?;
Ok(())
}
/// Translates the library marker copying.
pub fn library_marker<D>(
context: &mut revive_llvm_context::PolkaVMContext<D>,
offset: u64,
value: u64,
) -> anyhow::Result<()>
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
revive_llvm_context::polkavm_evm_memory::store_byte(
context,
context.word_const(offset),
context.word_const(value),
)?;
Ok(())
}
/// Translates the static data copying.
pub fn static_data<'ctx, D>(
context: &mut revive_llvm_context::PolkaVMContext<'ctx, D>,
destination: inkwell::values::IntValue<'ctx>,
source: &str,
) -> anyhow::Result<()>
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
let mut offset = 0;
for (index, chunk) in source
.chars()
.collect::<Vec<char>>()
.chunks(revive_common::BYTE_LENGTH_WORD * 2)
.enumerate()
{
let mut value_string = chunk.iter().collect::<String>();
value_string.push_str(
"0".repeat((revive_common::BYTE_LENGTH_WORD * 2) - chunk.len())
.as_str(),
);
let datacopy_destination = context.builder().build_int_add(
destination,
context.word_const(offset as u64),
format!("datacopy_destination_index_{index}").as_str(),
)?;
let datacopy_value = context.word_const_str_hex(value_string.as_str());
revive_llvm_context::polkavm_evm_memory::store(
context,
datacopy_destination,
datacopy_value,
)?;
offset += chunk.len() / 2;
}
Ok(())
}
@@ -1,69 +0,0 @@
//! Translates the jump operations.
/// Translates the unconditional jump.
pub fn unconditional<D>(
context: &mut revive_llvm_context::PolkaVMContext<D>,
destination: num::BigUint,
stack_hash: md5::Digest,
) -> anyhow::Result<()>
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
let code_type = context
.code_type()
.ok_or_else(|| anyhow::anyhow!("The contract code part type is undefined"))?;
let block_key = revive_llvm_context::PolkaVMFunctionBlockKey::new(code_type, destination);
let block = context
.current_function()
.borrow()
.evmla()
.find_block(&block_key, &stack_hash)?;
context.build_unconditional_branch(block.inner());
Ok(())
}
/// Translates the conditional jump.
pub fn conditional<D>(
context: &mut revive_llvm_context::PolkaVMContext<D>,
destination: num::BigUint,
stack_hash: md5::Digest,
stack_height: usize,
) -> anyhow::Result<()>
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
let code_type = context
.code_type()
.ok_or_else(|| anyhow::anyhow!("The contract code part type is undefined"))?;
let block_key = revive_llvm_context::PolkaVMFunctionBlockKey::new(code_type, destination);
let condition_pointer = context.evmla().stack[stack_height]
.to_llvm()
.into_pointer_value();
let condition = context.build_load(
revive_llvm_context::PolkaVMPointer::new_stack_field(context, condition_pointer),
format!("conditional_{block_key}_condition").as_str(),
)?;
let condition = context.builder().build_int_compare(
inkwell::IntPredicate::NE,
condition.into_int_value(),
context.word_const(0),
format!("conditional_{block_key}_condition_compared").as_str(),
)?;
let then_block = context
.current_function()
.borrow()
.evmla()
.find_block(&block_key, &stack_hash)?;
let join_block =
context.append_basic_block(format!("conditional_{block_key}_join_block").as_str());
context.build_conditional_branch(condition, then_block.inner(), join_block)?;
context.set_basic_block(join_block);
Ok(())
}
@@ -1,382 +0,0 @@
//! The EVM instruction.
pub mod codecopy;
pub mod jump;
pub mod name;
pub mod stack;
use std::collections::BTreeMap;
use serde::Deserialize;
use serde::Serialize;
use self::name::Name;
/// The EVM instruction.
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Instruction {
/// The opcode or tag identifier.
pub name: Name,
/// The optional value argument.
pub value: Option<String>,
/// The source code identifier.
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<isize>,
/// The source code location begin.
pub begin: isize,
/// The source code location end.
pub end: isize,
}
impl Instruction {
/// Returns the number of input stack arguments.
pub const fn input_size(&self, version: &semver::Version) -> usize {
match self.name {
Name::POP => 1,
Name::JUMP => 1,
Name::JUMPI => 2,
Name::ADD => 2,
Name::SUB => 2,
Name::MUL => 2,
Name::DIV => 2,
Name::MOD => 2,
Name::SDIV => 2,
Name::SMOD => 2,
Name::LT => 2,
Name::GT => 2,
Name::EQ => 2,
Name::ISZERO => 1,
Name::SLT => 2,
Name::SGT => 2,
Name::OR => 2,
Name::XOR => 2,
Name::NOT => 1,
Name::AND => 2,
Name::SHL => 2,
Name::SHR => 2,
Name::SAR => 2,
Name::BYTE => 2,
Name::ADDMOD => 3,
Name::MULMOD => 3,
Name::EXP => 2,
Name::SIGNEXTEND => 2,
Name::SHA3 => 2,
Name::KECCAK256 => 2,
Name::MLOAD => 1,
Name::MSTORE => 2,
Name::MSTORE8 => 2,
Name::MCOPY => 3,
Name::SLOAD => 1,
Name::SSTORE => 2,
Name::TLOAD => 1,
Name::TSTORE => 2,
Name::PUSHIMMUTABLE => 0,
Name::ASSIGNIMMUTABLE => {
if version.minor >= 8 {
2
} else {
1
}
}
Name::CALLDATALOAD => 1,
Name::CALLDATACOPY => 3,
Name::CODECOPY => 3,
Name::RETURNDATACOPY => 3,
Name::EXTCODESIZE => 1,
Name::EXTCODEHASH => 1,
Name::CALL => 7,
Name::CALLCODE => 7,
Name::STATICCALL => 6,
Name::DELEGATECALL => 6,
Name::RETURN => 2,
Name::REVERT => 2,
Name::SELFDESTRUCT => 1,
Name::LOG0 => 2,
Name::LOG1 => 3,
Name::LOG2 => 4,
Name::LOG3 => 5,
Name::LOG4 => 6,
Name::CREATE => 3,
Name::CREATE2 => 4,
Name::ZK_CREATE => 3,
Name::ZK_CREATE2 => 4,
Name::BALANCE => 1,
Name::BLOCKHASH => 1,
Name::BLOBHASH => 1,
Name::EXTCODECOPY => 4,
Name::RecursiveCall { input_size, .. } => input_size,
Name::RecursiveReturn { input_size } => input_size,
_ => 0,
}
}
/// Returns the number of output stack arguments.
pub const fn output_size(&self) -> usize {
match self.name {
Name::PUSH => 1,
Name::PUSH_Data => 1,
Name::PUSH_Tag => 1,
Name::PUSH_ContractHash => 1,
Name::PUSH_ContractHashSize => 1,
Name::PUSHLIB => 1,
Name::PUSHDEPLOYADDRESS => 1,
Name::PUSH1 => 1,
Name::PUSH2 => 1,
Name::PUSH3 => 1,
Name::PUSH4 => 1,
Name::PUSH5 => 1,
Name::PUSH6 => 1,
Name::PUSH7 => 1,
Name::PUSH8 => 1,
Name::PUSH9 => 1,
Name::PUSH10 => 1,
Name::PUSH11 => 1,
Name::PUSH12 => 1,
Name::PUSH13 => 1,
Name::PUSH14 => 1,
Name::PUSH15 => 1,
Name::PUSH16 => 1,
Name::PUSH17 => 1,
Name::PUSH18 => 1,
Name::PUSH19 => 1,
Name::PUSH20 => 1,
Name::PUSH21 => 1,
Name::PUSH22 => 1,
Name::PUSH23 => 1,
Name::PUSH24 => 1,
Name::PUSH25 => 1,
Name::PUSH26 => 1,
Name::PUSH27 => 1,
Name::PUSH28 => 1,
Name::PUSH29 => 1,
Name::PUSH30 => 1,
Name::PUSH31 => 1,
Name::PUSH32 => 1,
Name::DUP1 => 1,
Name::DUP2 => 1,
Name::DUP3 => 1,
Name::DUP4 => 1,
Name::DUP5 => 1,
Name::DUP6 => 1,
Name::DUP7 => 1,
Name::DUP8 => 1,
Name::DUP9 => 1,
Name::DUP10 => 1,
Name::DUP11 => 1,
Name::DUP12 => 1,
Name::DUP13 => 1,
Name::DUP14 => 1,
Name::DUP15 => 1,
Name::DUP16 => 1,
Name::ADD => 1,
Name::SUB => 1,
Name::MUL => 1,
Name::DIV => 1,
Name::MOD => 1,
Name::SDIV => 1,
Name::SMOD => 1,
Name::LT => 1,
Name::GT => 1,
Name::EQ => 1,
Name::ISZERO => 1,
Name::SLT => 1,
Name::SGT => 1,
Name::OR => 1,
Name::XOR => 1,
Name::NOT => 1,
Name::AND => 1,
Name::SHL => 1,
Name::SHR => 1,
Name::SAR => 1,
Name::BYTE => 1,
Name::ADDMOD => 1,
Name::MULMOD => 1,
Name::EXP => 1,
Name::SIGNEXTEND => 1,
Name::SHA3 => 1,
Name::KECCAK256 => 1,
Name::MLOAD => 1,
Name::SLOAD => 1,
Name::TLOAD => 1,
Name::PUSHIMMUTABLE => 1,
Name::CALLDATALOAD => 1,
Name::CALLDATASIZE => 1,
Name::CODESIZE => 1,
Name::PUSHSIZE => 1,
Name::RETURNDATASIZE => 1,
Name::EXTCODESIZE => 1,
Name::EXTCODEHASH => 1,
Name::CALL => 1,
Name::CALLCODE => 1,
Name::STATICCALL => 1,
Name::DELEGATECALL => 1,
Name::CREATE => 1,
Name::CREATE2 => 1,
Name::ZK_CREATE => 1,
Name::ZK_CREATE2 => 1,
Name::ADDRESS => 1,
Name::CALLER => 1,
Name::TIMESTAMP => 1,
Name::NUMBER => 1,
Name::CALLVALUE => 1,
Name::GAS => 1,
Name::BALANCE => 1,
Name::SELFBALANCE => 1,
Name::GASLIMIT => 1,
Name::GASPRICE => 1,
Name::ORIGIN => 1,
Name::CHAINID => 1,
Name::BLOCKHASH => 1,
Name::BLOBHASH => 1,
Name::DIFFICULTY => 1,
Name::PREVRANDAO => 1,
Name::COINBASE => 1,
Name::MSIZE => 1,
Name::BASEFEE => 1,
Name::BLOBBASEFEE => 1,
Name::PC => 1,
Name::RecursiveCall { output_size, .. } => output_size,
_ => 0,
}
}
/// Replaces the instruction data aliases with the actual data.
pub fn replace_data_aliases(
instructions: &mut [Self],
mapping: &BTreeMap<String, String>,
) -> anyhow::Result<()> {
for instruction in instructions.iter_mut() {
match instruction {
Instruction {
name: Name::PUSH_ContractHash | Name::PUSH_ContractHashSize,
value: Some(value),
..
} => {
*value = mapping.get(value.as_str()).cloned().ok_or_else(|| {
anyhow::anyhow!("Contract alias `{}` data not found", value)
})?;
}
Instruction {
name: Name::PUSH_Data,
value: Some(value),
..
} => {
let mut key_extended =
"0".repeat(revive_common::BYTE_LENGTH_WORD * 2 - value.len());
key_extended.push_str(value.as_str());
*value = mapping.get(key_extended.as_str()).cloned().ok_or_else(|| {
anyhow::anyhow!("Data chunk alias `{}` data not found", key_extended)
})?;
}
_ => {}
}
}
Ok(())
}
/// Initializes an `INVALID` instruction to terminate an invalid unreachable block part.
pub fn invalid(previous: &Self) -> Self {
Self {
name: Name::INVALID,
value: None,
source: previous.source,
begin: previous.begin,
end: previous.end,
}
}
/// Initializes a recursive function `Call` instruction.
pub fn recursive_call(
name: String,
entry_key: revive_llvm_context::PolkaVMFunctionBlockKey,
stack_hash: md5::Digest,
input_size: usize,
output_size: usize,
return_address: revive_llvm_context::PolkaVMFunctionBlockKey,
previous: &Self,
) -> Self {
Self {
name: Name::RecursiveCall {
name,
entry_key,
stack_hash,
input_size,
output_size,
return_address,
},
value: None,
source: previous.source,
begin: previous.begin,
end: previous.end,
}
}
/// Initializes a recursive function `Return` instruction.
pub fn recursive_return(input_size: usize, previous: &Self) -> Self {
Self {
name: Name::RecursiveReturn { input_size },
value: None,
source: previous.source,
begin: previous.begin,
end: previous.end,
}
}
}
impl std::fmt::Display for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = self.name.to_string();
match self.name {
Name::Tag => write!(f, "{:4}", name),
_ => write!(f, "{:15}", name),
}?;
match self.value {
Some(ref value) if value.len() <= 64 => write!(f, "{}", value)?,
Some(ref value) => write!(f, "... {}", &value[value.len() - 60..])?,
None => {}
}
Ok(())
}
}
@@ -1,417 +0,0 @@
//! The EVM instruction name.
use serde::Deserialize;
use serde::Serialize;
/// The EVM instruction name.
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types)]
#[allow(clippy::upper_case_acronyms)]
pub enum Name {
/// The eponymous EVM instruction.
PUSH,
/// Pushes a constant tag index.
#[serde(rename = "PUSH [tag]")]
PUSH_Tag,
/// Pushes an unknown `data` value.
#[serde(rename = "PUSH data")]
PUSH_Data,
/// Pushes a contract hash size.
#[serde(rename = "PUSH #[$]")]
PUSH_ContractHashSize,
/// Pushes a contract hash.
#[serde(rename = "PUSH [$]")]
PUSH_ContractHash,
/// The eponymous EVM instruction.
PUSH1,
/// The eponymous EVM instruction.
PUSH2,
/// The eponymous EVM instruction.
PUSH3,
/// The eponymous EVM instruction.
PUSH4,
/// The eponymous EVM instruction.
PUSH5,
/// The eponymous EVM instruction.
PUSH6,
/// The eponymous EVM instruction.
PUSH7,
/// The eponymous EVM instruction.
PUSH8,
/// The eponymous EVM instruction.
PUSH9,
/// The eponymous EVM instruction.
PUSH10,
/// The eponymous EVM instruction.
PUSH11,
/// The eponymous EVM instruction.
PUSH12,
/// The eponymous EVM instruction.
PUSH13,
/// The eponymous EVM instruction.
PUSH14,
/// The eponymous EVM instruction.
PUSH15,
/// The eponymous EVM instruction.
PUSH16,
/// The eponymous EVM instruction.
PUSH17,
/// The eponymous EVM instruction.
PUSH18,
/// The eponymous EVM instruction.
PUSH19,
/// The eponymous EVM instruction.
PUSH20,
/// The eponymous EVM instruction.
PUSH21,
/// The eponymous EVM instruction.
PUSH22,
/// The eponymous EVM instruction.
PUSH23,
/// The eponymous EVM instruction.
PUSH24,
/// The eponymous EVM instruction.
PUSH25,
/// The eponymous EVM instruction.
PUSH26,
/// The eponymous EVM instruction.
PUSH27,
/// The eponymous EVM instruction.
PUSH28,
/// The eponymous EVM instruction.
PUSH29,
/// The eponymous EVM instruction.
PUSH30,
/// The eponymous EVM instruction.
PUSH31,
/// The eponymous EVM instruction.
PUSH32,
/// The eponymous EVM instruction.
DUP1,
/// The eponymous EVM instruction.
DUP2,
/// The eponymous EVM instruction.
DUP3,
/// The eponymous EVM instruction.
DUP4,
/// The eponymous EVM instruction.
DUP5,
/// The eponymous EVM instruction.
DUP6,
/// The eponymous EVM instruction.
DUP7,
/// The eponymous EVM instruction.
DUP8,
/// The eponymous EVM instruction.
DUP9,
/// The eponymous EVM instruction.
DUP10,
/// The eponymous EVM instruction.
DUP11,
/// The eponymous EVM instruction.
DUP12,
/// The eponymous EVM instruction.
DUP13,
/// The eponymous EVM instruction.
DUP14,
/// The eponymous EVM instruction.
DUP15,
/// The eponymous EVM instruction.
DUP16,
/// The eponymous EVM instruction.
SWAP1,
/// The eponymous EVM instruction.
SWAP2,
/// The eponymous EVM instruction.
SWAP3,
/// The eponymous EVM instruction.
SWAP4,
/// The eponymous EVM instruction.
SWAP5,
/// The eponymous EVM instruction.
SWAP6,
/// The eponymous EVM instruction.
SWAP7,
/// The eponymous EVM instruction.
SWAP8,
/// The eponymous EVM instruction.
SWAP9,
/// The eponymous EVM instruction.
SWAP10,
/// The eponymous EVM instruction.
SWAP11,
/// The eponymous EVM instruction.
SWAP12,
/// The eponymous EVM instruction.
SWAP13,
/// The eponymous EVM instruction.
SWAP14,
/// The eponymous EVM instruction.
SWAP15,
/// The eponymous EVM instruction.
SWAP16,
/// The eponymous EVM instruction.
POP,
/// Sets the current basic code block.
#[serde(rename = "tag")]
Tag,
/// The eponymous EVM instruction.
JUMP,
/// The eponymous EVM instruction.
JUMPI,
/// The eponymous EVM instruction.
JUMPDEST,
/// The eponymous EVM instruction.
ADD,
/// The eponymous EVM instruction.
SUB,
/// The eponymous EVM instruction.
MUL,
/// The eponymous EVM instruction.
DIV,
/// The eponymous EVM instruction.
MOD,
/// The eponymous EVM instruction.
SDIV,
/// The eponymous EVM instruction.
SMOD,
/// The eponymous EVM instruction.
LT,
/// The eponymous EVM instruction.
GT,
/// The eponymous EVM instruction.
EQ,
/// The eponymous EVM instruction.
ISZERO,
/// The eponymous EVM instruction.
SLT,
/// The eponymous EVM instruction.
SGT,
/// The eponymous EVM instruction.
OR,
/// The eponymous EVM instruction.
XOR,
/// The eponymous EVM instruction.
NOT,
/// The eponymous EVM instruction.
AND,
/// The eponymous EVM instruction.
SHL,
/// The eponymous EVM instruction.
SHR,
/// The eponymous EVM instruction.
SAR,
/// The eponymous EVM instruction.
BYTE,
/// The eponymous EVM instruction.
ADDMOD,
/// The eponymous EVM instruction.
MULMOD,
/// The eponymous EVM instruction.
EXP,
/// The eponymous EVM instruction.
SIGNEXTEND,
/// The eponymous EVM instruction.
SHA3,
/// The eponymous EVM instruction.
KECCAK256,
/// The eponymous EVM instruction.
MLOAD,
/// The eponymous EVM instruction.
MSTORE,
/// The eponymous EVM instruction.
MSTORE8,
/// The eponymous EVM instruction.
MCOPY,
/// The eponymous EVM instruction.
SLOAD,
/// The eponymous EVM instruction.
SSTORE,
/// The eponymous EVM instruction.
TLOAD,
/// The eponymous EVM instruction.
TSTORE,
/// The eponymous EVM instruction.
PUSHIMMUTABLE,
/// The eponymous EVM instruction.
ASSIGNIMMUTABLE,
/// The eponymous EVM instruction.
CALLDATALOAD,
/// The eponymous EVM instruction.
CALLDATASIZE,
/// The eponymous EVM instruction.
CALLDATACOPY,
/// The eponymous EVM instruction.
CODESIZE,
/// The eponymous EVM instruction.
CODECOPY,
/// The eponymous EVM instruction.
PUSHSIZE,
/// The eponymous EVM instruction.
EXTCODESIZE,
/// The eponymous EVM instruction.
EXTCODEHASH,
/// The eponymous EVM instruction.
RETURNDATASIZE,
/// The eponymous EVM instruction.
RETURNDATACOPY,
/// The eponymous EVM instruction.
RETURN,
/// The eponymous EVM instruction.
REVERT,
/// The eponymous EVM instruction.
STOP,
/// The eponymous EVM instruction.
INVALID,
/// The eponymous EVM instruction.
LOG0,
/// The eponymous EVM instruction.
LOG1,
/// The eponymous EVM instruction.
LOG2,
/// The eponymous EVM instruction.
LOG3,
/// The eponymous EVM instruction.
LOG4,
/// The eponymous EVM instruction.
CALL,
/// The eponymous EVM instruction.
STATICCALL,
/// The eponymous EVM instruction.
DELEGATECALL,
/// The eponymous EVM instruction.
CREATE,
/// The eponymous EVM instruction.
CREATE2,
/// The eponymous PolkaVM instruction.
#[serde(rename = "$ZK_CREATE")]
ZK_CREATE,
/// The eponymous PolkaVM instruction.
#[serde(rename = "$ZK_CREATE2")]
ZK_CREATE2,
/// The eponymous EVM instruction.
ADDRESS,
/// The eponymous EVM instruction.
CALLER,
/// The eponymous EVM instruction.
CALLVALUE,
/// The eponymous EVM instruction.
GAS,
/// The eponymous EVM instruction.
BALANCE,
/// The eponymous EVM instruction.
SELFBALANCE,
/// The eponymous EVM instruction.
PUSHLIB,
/// The eponymous EVM instruction.
PUSHDEPLOYADDRESS,
/// The eponymous EVM instruction.
GASLIMIT,
/// The eponymous EVM instruction.
GASPRICE,
/// The eponymous EVM instruction.
ORIGIN,
/// The eponymous EVM instruction.
CHAINID,
/// The eponymous EVM instruction.
TIMESTAMP,
/// The eponymous EVM instruction.
NUMBER,
/// The eponymous EVM instruction.
BLOCKHASH,
/// The eponymous EVM instruction.
BLOBHASH,
/// The eponymous EVM instruction.
DIFFICULTY,
/// The eponymous EVM instruction.
PREVRANDAO,
/// The eponymous EVM instruction.
COINBASE,
/// The eponymous EVM instruction.
BASEFEE,
/// The eponymous EVM instruction.
BLOBBASEFEE,
/// The eponymous EVM instruction.
MSIZE,
/// The eponymous EVM instruction.
CALLCODE,
/// The eponymous EVM instruction.
PC,
/// The eponymous EVM instruction.
EXTCODECOPY,
/// The eponymous EVM instruction.
SELFDESTRUCT,
/// The recursive function call instruction.
#[serde(skip)]
RecursiveCall {
/// The called function name.
name: String,
/// The called function key.
entry_key: revive_llvm_context::PolkaVMFunctionBlockKey,
/// The stack state hash after return.
stack_hash: md5::Digest,
/// The input size.
input_size: usize,
/// The output size.
output_size: usize,
/// The return address.
return_address: revive_llvm_context::PolkaVMFunctionBlockKey,
},
/// The recursive function return instruction.
#[serde(skip)]
RecursiveReturn {
/// The output size.
input_size: usize,
},
}
impl std::fmt::Display for Name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Tag => write!(f, "Tag"),
Self::RecursiveCall {
name,
entry_key,
input_size,
output_size,
return_address,
..
} => write!(
f,
"RECURSIVE_CALL({}_{}, {}, {}, {})",
name, entry_key, input_size, output_size, return_address
),
Self::RecursiveReturn { input_size } => write!(f, "RECURSIVE_RETURN({})", input_size),
_ => write!(
f,
"{}",
serde_json::to_string(self)
.expect("Always valid")
.trim_matches('\"')
),
}
}
}
@@ -1,106 +0,0 @@
//! Translates the stack memory operations.
use inkwell::values::BasicValue;
/// Translates the ordinar value push.
pub fn push<'ctx, D>(
context: &mut revive_llvm_context::PolkaVMContext<'ctx, D>,
value: String,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
let result = context
.word_type()
.const_int_from_string(
value.to_ascii_uppercase().as_str(),
inkwell::types::StringRadix::Hexadecimal,
)
.expect("Always valid")
.as_basic_value_enum();
Ok(result)
}
/// Translates the block tag label push.
pub fn push_tag<'ctx, D>(
context: &mut revive_llvm_context::PolkaVMContext<'ctx, D>,
value: String,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
let result = context
.word_type()
.const_int_from_string(value.as_str(), inkwell::types::StringRadix::Decimal)
.expect("Always valid");
Ok(result.as_basic_value_enum())
}
/// Translates the stack memory duplicate.
pub fn dup<'ctx, D>(
context: &mut revive_llvm_context::PolkaVMContext<'ctx, D>,
offset: usize,
height: usize,
original: &mut Option<String>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
let element = &context.evmla().stack[height - offset - 1];
let value = context.build_load(
revive_llvm_context::PolkaVMPointer::new_stack_field(
context,
element.to_llvm().into_pointer_value(),
),
format!("dup{offset}").as_str(),
)?;
element.original.clone_into(original);
Ok(value)
}
/// Translates the stack memory swap.
pub fn swap<D>(
context: &mut revive_llvm_context::PolkaVMContext<D>,
offset: usize,
height: usize,
) -> anyhow::Result<()>
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
let top_element = context.evmla().stack[height - 1].to_owned();
let top_pointer = revive_llvm_context::PolkaVMPointer::new_stack_field(
context,
top_element.to_llvm().into_pointer_value(),
);
let top_value = context.build_load(top_pointer, format!("swap{offset}_top_value").as_str())?;
let swap_element = context.evmla().stack[height - offset - 1].to_owned();
let swap_pointer = revive_llvm_context::PolkaVMPointer::new_stack_field(
context,
swap_element.to_llvm().into_pointer_value(),
);
let swap_value =
context.build_load(swap_pointer, format!("swap{offset}_swap_value").as_str())?;
swap_element
.original
.clone_into(&mut context.evmla_mut().stack[height - 1].original);
top_element
.original
.clone_into(&mut context.evmla_mut().stack[height - offset - 1].original);
context.build_store(top_pointer, swap_value)?;
context.build_store(swap_pointer, top_value)?;
Ok(())
}
/// Translates the stack memory pop.
pub fn pop<D>(_context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()>
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
Ok(())
}
-295
View File
@@ -1,295 +0,0 @@
//! The `solc --asm-json` output.
pub mod data;
pub mod instruction;
use std::collections::BTreeMap;
use std::collections::HashSet;
use serde::Deserialize;
use serde::Serialize;
use sha3::Digest;
use crate::evmla::ethereal_ir::entry_link::EntryLink;
use crate::evmla::ethereal_ir::EtherealIR;
use crate::solc::standard_json::output::contract::evm::extra_metadata::ExtraMetadata;
use self::data::Data;
use self::instruction::name::Name as InstructionName;
use self::instruction::Instruction;
/// The JSON assembly.
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Assembly {
/// The metadata string.
#[serde(rename = ".auxdata")]
pub auxdata: Option<String>,
/// The deploy code instructions.
#[serde(rename = ".code")]
pub code: Option<Vec<Instruction>>,
/// The runtime code.
#[serde(rename = ".data")]
pub data: Option<BTreeMap<String, Data>>,
/// The full contract path.
#[serde(skip_serializing_if = "Option::is_none")]
pub full_path: Option<String>,
/// The factory dependency paths.
#[serde(default = "HashSet::new")]
pub factory_dependencies: HashSet<String>,
/// The EVMLA extra metadata.
#[serde(skip_serializing_if = "Option::is_none")]
pub extra_metadata: Option<ExtraMetadata>,
}
impl Assembly {
/// Gets the contract `keccak256` hash.
pub fn keccak256(&self) -> String {
let json = serde_json::to_vec(self).expect("Always valid");
hex::encode(sha3::Keccak256::digest(json.as_slice()))
}
/// Sets the full contract path.
pub fn set_full_path(&mut self, full_path: String) {
self.full_path = Some(full_path);
}
/// Returns the full contract path if it is set, or `<undefined>` otherwise.
/// # Panics
/// If the `full_path` has not been set.
pub fn full_path(&self) -> &str {
self.full_path
.as_deref()
.unwrap_or_else(|| panic!("The full path of some contracts is unset"))
}
/// Get the list of missing deployable libraries.
pub fn get_missing_libraries(&self) -> HashSet<String> {
let mut missing_libraries = HashSet::new();
if let Some(code) = self.code.as_ref() {
for instruction in code.iter() {
if let InstructionName::PUSHLIB = instruction.name {
let library_path = instruction.value.to_owned().expect("Always exists");
missing_libraries.insert(library_path);
}
}
}
if let Some(data) = self.data.as_ref() {
for (_, data) in data.iter() {
missing_libraries.extend(data.get_missing_libraries());
}
}
missing_libraries
}
/// Replaces the deploy code dependencies with full contract path and returns the list.
pub fn deploy_dependencies_pass(
&mut self,
full_path: &str,
hash_data_mapping: &BTreeMap<String, String>,
) -> anyhow::Result<BTreeMap<String, String>> {
let mut index_path_mapping = BTreeMap::new();
let index = "0".repeat(revive_common::BYTE_LENGTH_WORD * 2);
index_path_mapping.insert(index, full_path.to_owned());
let dependencies = match self.data.as_mut() {
Some(dependencies) => dependencies,
None => return Ok(index_path_mapping),
};
for (index, data) in dependencies.iter_mut() {
if index == "0" {
continue;
}
let mut index_extended = "0".repeat(revive_common::BYTE_LENGTH_WORD * 2 - index.len());
index_extended.push_str(index.as_str());
*data = match data {
Data::Assembly(assembly) => {
let hash = assembly.keccak256();
let full_path =
hash_data_mapping
.get(hash.as_str())
.cloned()
.ok_or_else(|| {
anyhow::anyhow!("Contract path not found for hash `{}`", hash)
})?;
self.factory_dependencies.insert(full_path.to_owned());
index_path_mapping.insert(index_extended, full_path.clone());
Data::Path(full_path)
}
Data::Hash(hash) => {
index_path_mapping.insert(index_extended, hash.to_owned());
continue;
}
_ => continue,
};
}
Ok(index_path_mapping)
}
/// Replaces the runtime code dependencies with full contract path and returns the list.
pub fn runtime_dependencies_pass(
&mut self,
full_path: &str,
hash_data_mapping: &BTreeMap<String, String>,
) -> anyhow::Result<BTreeMap<String, String>> {
let mut index_path_mapping = BTreeMap::new();
let index = "0".repeat(revive_common::BYTE_LENGTH_WORD * 2);
index_path_mapping.insert(index, full_path.to_owned());
let dependencies = match self
.data
.as_mut()
.and_then(|data| data.get_mut("0"))
.and_then(|data| data.get_assembly_mut())
.and_then(|assembly| assembly.data.as_mut())
{
Some(dependencies) => dependencies,
None => return Ok(index_path_mapping),
};
for (index, data) in dependencies.iter_mut() {
let mut index_extended = "0".repeat(revive_common::BYTE_LENGTH_WORD * 2 - index.len());
index_extended.push_str(index.as_str());
*data = match data {
Data::Assembly(assembly) => {
let hash = assembly.keccak256();
let full_path =
hash_data_mapping
.get(hash.as_str())
.cloned()
.ok_or_else(|| {
anyhow::anyhow!("Contract path not found for hash `{}`", hash)
})?;
self.factory_dependencies.insert(full_path.to_owned());
index_path_mapping.insert(index_extended, full_path.clone());
Data::Path(full_path)
}
Data::Hash(hash) => {
index_path_mapping.insert(index_extended, hash.to_owned());
continue;
}
_ => continue,
};
}
Ok(index_path_mapping)
}
}
impl<D> revive_llvm_context::PolkaVMWriteLLVM<D> for Assembly
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
fn declare(
&mut self,
context: &mut revive_llvm_context::PolkaVMContext<D>,
) -> anyhow::Result<()> {
let mut entry = revive_llvm_context::PolkaVMEntryFunction::default();
entry.declare(context)?;
revive_llvm_context::PolkaVMDeployCodeFunction::new(
revive_llvm_context::PolkaVMDummyLLVMWritable::default(),
)
.declare(context)?;
revive_llvm_context::PolkaVMRuntimeCodeFunction::new(
revive_llvm_context::PolkaVMDummyLLVMWritable::default(),
)
.declare(context)?;
revive_llvm_context::PolkaVMImmutableDataLoadFunction.declare(context)?;
entry.into_llvm(context)?;
Ok(())
}
fn into_llvm(
mut self,
context: &mut revive_llvm_context::PolkaVMContext<D>,
) -> anyhow::Result<()> {
let full_path = self.full_path().to_owned();
context
.debug_config()
.dump_evmla(full_path.as_str(), self.to_string().as_str())?;
let deploy_code_blocks = EtherealIR::get_blocks(
context.evmla().version.to_owned(),
revive_llvm_context::PolkaVMCodeType::Deploy,
self.code
.as_deref()
.ok_or_else(|| anyhow::anyhow!("Deploy code instructions not found"))?,
)?;
let data = self
.data
.ok_or_else(|| anyhow::anyhow!("Runtime code data not found"))?
.remove("0")
.expect("Always exists");
context
.debug_config()
.dump_evmla(full_path.as_str(), data.to_string().as_str())?;
let runtime_code_instructions = match data {
Data::Assembly(assembly) => assembly
.code
.ok_or_else(|| anyhow::anyhow!("Runtime code instructions not found"))?,
Data::Hash(hash) => {
anyhow::bail!("Expected runtime code instructions, found hash `{}`", hash)
}
Data::Path(path) => {
anyhow::bail!("Expected runtime code instructions, found path `{}`", path)
}
};
let runtime_code_blocks = EtherealIR::get_blocks(
context.evmla().version.to_owned(),
revive_llvm_context::PolkaVMCodeType::Runtime,
runtime_code_instructions.as_slice(),
)?;
let extra_metadata = self.extra_metadata.take().unwrap_or_default();
let mut blocks = deploy_code_blocks;
blocks.extend(runtime_code_blocks);
let mut ethereal_ir =
EtherealIR::new(context.evmla().version.to_owned(), extra_metadata, blocks)?;
context
.debug_config()
.dump_ethir(full_path.as_str(), ethereal_ir.to_string().as_str())?;
ethereal_ir.declare(context)?;
ethereal_ir.into_llvm(context)?;
revive_llvm_context::PolkaVMDeployCodeFunction::new(EntryLink::new(
revive_llvm_context::PolkaVMCodeType::Deploy,
))
.into_llvm(context)?;
revive_llvm_context::PolkaVMRuntimeCodeFunction::new(EntryLink::new(
revive_llvm_context::PolkaVMCodeType::Runtime,
))
.into_llvm(context)?;
revive_llvm_context::PolkaVMImmutableDataLoadFunction.into_llvm(context)?;
Ok(())
}
}
impl std::fmt::Display for Assembly {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(instructions) = self.code.as_ref() {
for (index, instruction) in instructions.iter().enumerate() {
match instruction.name {
InstructionName::Tag => writeln!(f, "{index:03} {instruction}")?,
_ => writeln!(f, "{index:03} {instruction}")?,
}
}
}
Ok(())
}
}
@@ -1,48 +0,0 @@
//! The Ethereal IR entry function link.
use inkwell::values::BasicValue;
use crate::evmla::ethereal_ir::EtherealIR;
/// The Ethereal IR entry function link.
/// The link represents branching between the deploy and runtime code.
#[derive(Debug, Clone)]
pub struct EntryLink {
/// The code part type.
pub code_type: revive_llvm_context::PolkaVMCodeType,
}
impl EntryLink {
/// A shortcut constructor.
pub fn new(code_type: revive_llvm_context::PolkaVMCodeType) -> Self {
Self { code_type }
}
}
impl<D> revive_llvm_context::PolkaVMWriteLLVM<D> for EntryLink
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> {
let target = context
.get_function(EtherealIR::DEFAULT_ENTRY_FUNCTION_NAME)
.expect("Always exists")
.borrow()
.declaration();
let is_deploy_code = match self.code_type {
revive_llvm_context::PolkaVMCodeType::Deploy => context
.integer_type(revive_common::BIT_LENGTH_BOOLEAN)
.const_int(1, false),
revive_llvm_context::PolkaVMCodeType::Runtime => context
.integer_type(revive_common::BIT_LENGTH_BOOLEAN)
.const_int(0, false),
};
context.build_call(
target,
&[is_deploy_code.as_basic_value_enum()],
format!("call_link_{}", EtherealIR::DEFAULT_ENTRY_FUNCTION_NAME).as_str(),
);
Ok(())
}
}
File diff suppressed because it is too large Load Diff
@@ -1,37 +0,0 @@
//! The Ethereal IR block element stack element.
/// The Ethereal IR block element stack element.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Element {
/// The runtime value.
Value(String),
/// The compile-time value.
Constant(num::BigUint),
/// The compile-time destination tag.
Tag(num::BigUint),
/// The compile-time path.
Path(String),
/// The compile-time hexadecimal data chunk.
Data(String),
/// The recursive function return address.
ReturnAddress(usize),
}
impl Element {
pub fn value(identifier: String) -> Self {
Self::Value(identifier)
}
}
impl std::fmt::Display for Element {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Value(identifier) => write!(f, "V_{identifier}"),
Self::Constant(value) => write!(f, "{value:X}"),
Self::Tag(tag) => write!(f, "T_{tag}"),
Self::Path(path) => write!(f, "{path}"),
Self::Data(data) => write!(f, "{data}"),
Self::ReturnAddress(_) => write!(f, "RETURN_ADDRESS"),
}
}
}
@@ -1,120 +0,0 @@
//! The Ethereal IR block element stack.
pub mod element;
use self::element::Element;
/// The Ethereal IR block element stack.
#[derive(Debug, Default, Clone)]
pub struct Stack {
/// The stack elements.
pub elements: Vec<Element>,
}
impl Stack {
/// The default stack size.
pub const DEFAULT_STACK_SIZE: usize = 16;
/// A shortcut constructor.
pub fn new() -> Self {
Self {
elements: Vec::with_capacity(Self::DEFAULT_STACK_SIZE),
}
}
/// A shortcut constructor.
pub fn with_capacity(capacity: usize) -> Self {
Self {
elements: Vec::with_capacity(capacity),
}
}
/// A shortcut constructor.
pub fn new_with_elements(elements: Vec<Element>) -> Self {
Self { elements }
}
/// The stack state hash, which acts as a block identifier.
/// Each block clone has its own initial stack state, which uniquely identifies the block.
pub fn hash(&self) -> md5::Digest {
let mut hash_context = md5::Context::new();
for element in self.elements.iter() {
match element {
Element::Tag(tag) => hash_context.consume(tag.to_bytes_be()),
_ => hash_context.consume([0]),
}
}
hash_context.compute()
}
/// Pushes an element onto the stack.
pub fn push(&mut self, element: Element) {
self.elements.push(element);
}
/// Appends another stack on top of this one.
pub fn append(&mut self, other: &mut Self) {
self.elements.append(&mut other.elements);
}
/// Pops a stack element.
pub fn pop(&mut self) -> anyhow::Result<Element> {
self.elements
.pop()
.ok_or_else(|| anyhow::anyhow!("Stack underflow"))
}
/// Pops the tag from the top.
pub fn pop_tag(&mut self) -> anyhow::Result<num::BigUint> {
match self.elements.pop() {
Some(Element::Tag(tag)) => Ok(tag),
Some(element) => anyhow::bail!("Expected tag, found {}", element),
None => anyhow::bail!("Stack underflow"),
}
}
/// Swaps two stack elements.
pub fn swap(&mut self, index: usize) -> anyhow::Result<()> {
if self.elements.len() < index + 1 {
anyhow::bail!("Stack underflow");
}
let length = self.elements.len();
self.elements.swap(length - 1, length - 1 - index);
Ok(())
}
/// Duplicates a stack element.
pub fn dup(&mut self, index: usize) -> anyhow::Result<Element> {
if self.elements.len() < index {
anyhow::bail!("Stack underflow");
}
Ok(self.elements[self.elements.len() - index].to_owned())
}
/// Returns the stack length.
pub fn len(&self) -> usize {
self.elements.len()
}
/// Returns an emptiness flag.
pub fn is_empty(&self) -> bool {
self.elements.len() == 0
}
}
impl std::fmt::Display for Stack {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"[ {} ]",
self.elements
.iter()
.map(Element::to_string)
.collect::<Vec<String>>()
.join(" | ")
)
}
}
@@ -1,156 +0,0 @@
//! The Ethereal IR block.
pub mod element;
use std::collections::HashSet;
use num::Zero;
use crate::evmla::assembly::instruction::name::Name as InstructionName;
use crate::evmla::assembly::instruction::Instruction;
use self::element::stack::Stack as ElementStack;
use self::element::Element;
/// The Ethereal IR block.
#[derive(Debug, Clone)]
pub struct Block {
/// The Solidity compiler version.
pub solc_version: semver::Version,
/// The block key.
pub key: revive_llvm_context::PolkaVMFunctionBlockKey,
/// The block instance.
pub instance: Option<usize>,
/// The block elements relevant to the stack consistency.
pub elements: Vec<Element>,
/// The block predecessors.
pub predecessors: HashSet<(revive_llvm_context::PolkaVMFunctionBlockKey, usize)>,
/// The initial stack state.
pub initial_stack: ElementStack,
/// The stack.
pub stack: ElementStack,
/// The extra block hashes for alternative routes.
pub extra_hashes: Vec<md5::Digest>,
}
impl Block {
/// The elements vector initial capacity.
pub const ELEMENTS_VECTOR_DEFAULT_CAPACITY: usize = 64;
/// The predecessors hashset initial capacity.
pub const PREDECESSORS_HASHSET_DEFAULT_CAPACITY: usize = 4;
/// Assembles a block from the sequence of instructions.
pub fn try_from_instructions(
solc_version: semver::Version,
code_type: revive_llvm_context::PolkaVMCodeType,
slice: &[Instruction],
) -> anyhow::Result<(Self, usize)> {
let mut cursor = 0;
let tag: num::BigUint = match slice[cursor].name {
InstructionName::Tag => {
let tag = slice[cursor]
.value
.as_deref()
.expect("Always exists")
.parse()
.expect("Always valid");
cursor += 1;
tag
}
_ => num::BigUint::zero(),
};
let mut block = Self {
solc_version: solc_version.clone(),
key: revive_llvm_context::PolkaVMFunctionBlockKey::new(code_type, tag),
instance: None,
elements: Vec::with_capacity(Self::ELEMENTS_VECTOR_DEFAULT_CAPACITY),
predecessors: HashSet::with_capacity(Self::PREDECESSORS_HASHSET_DEFAULT_CAPACITY),
initial_stack: ElementStack::new(),
stack: ElementStack::new(),
extra_hashes: vec![],
};
let mut dead_code = false;
while cursor < slice.len() {
if !dead_code {
let element: Element = Element::new(solc_version.clone(), slice[cursor].to_owned());
block.elements.push(element);
}
match slice[cursor].name {
InstructionName::RETURN
| InstructionName::REVERT
| InstructionName::STOP
| InstructionName::INVALID => {
cursor += 1;
dead_code = true;
}
InstructionName::JUMP => {
cursor += 1;
dead_code = true;
}
InstructionName::Tag => {
break;
}
_ => {
cursor += 1;
}
}
}
Ok((block, cursor))
}
/// Inserts a predecessor tag.
pub fn insert_predecessor(
&mut self,
key: revive_llvm_context::PolkaVMFunctionBlockKey,
instance: usize,
) {
self.predecessors.insert((key, instance));
}
}
impl<D> revive_llvm_context::PolkaVMWriteLLVM<D> for Block
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> {
context.set_code_type(self.key.code_type);
for element in self.elements.into_iter() {
element.into_llvm(context)?;
}
Ok(())
}
}
impl std::fmt::Display for Block {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(
f,
"block_{}/{}: {}",
self.key,
self.instance.unwrap_or_default(),
if self.predecessors.is_empty() {
"".to_owned()
} else {
format!(
"(predecessors: {})",
self.predecessors
.iter()
.map(|(key, instance)| format!("{}/{}", key, instance))
.collect::<Vec<String>>()
.join(", ")
)
},
)?;
for element in self.elements.iter() {
writeln!(f, " {element}")?;
}
Ok(())
}
}
File diff suppressed because it is too large Load Diff
@@ -1,29 +0,0 @@
//! The Ethereal IR block queue element.
use crate::evmla::ethereal_ir::function::block::element::stack::Stack;
/// The Ethereal IR block queue element.
#[derive(Debug, Clone)]
pub struct QueueElement {
/// The block key.
pub block_key: revive_llvm_context::PolkaVMFunctionBlockKey,
/// The block predecessor.
pub predecessor: Option<(revive_llvm_context::PolkaVMFunctionBlockKey, usize)>,
/// The predecessor's last stack state.
pub stack: Stack,
}
impl QueueElement {
/// A shortcut constructor.
pub fn new(
block_key: revive_llvm_context::PolkaVMFunctionBlockKey,
predecessor: Option<(revive_llvm_context::PolkaVMFunctionBlockKey, usize)>,
stack: Stack,
) -> Self {
Self {
block_key,
predecessor,
stack,
}
}
}
@@ -1,41 +0,0 @@
//! The Ethereal IR function type.
/// The Ethereal IR function type.
#[derive(Debug, Clone)]
pub enum Type {
/// The initial function, combining deploy and runtime code.
Initial,
/// The recursive function with a specific block starting its recursive context.
Recursive {
/// The function name.
name: String,
/// The function initial block key.
block_key: revive_llvm_context::PolkaVMFunctionBlockKey,
/// The size of stack input (in cells or 256-bit words).
input_size: usize,
/// The size of stack output (in cells or 256-bit words).
output_size: usize,
},
}
impl Type {
/// A shortcut constructor.
pub fn new_initial() -> Self {
Self::Initial
}
/// A shortcut constructor.
pub fn new_recursive(
name: String,
block_key: revive_llvm_context::PolkaVMFunctionBlockKey,
input_size: usize,
output_size: usize,
) -> Self {
Self::Recursive {
name,
block_key,
input_size,
output_size,
}
}
}
@@ -1,65 +0,0 @@
//! The Ethereal IR block visited element.
use std::cmp::Ordering;
/// The Ethereal IR block visited element.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VisitedElement {
/// The block key.
pub block_key: revive_llvm_context::PolkaVMFunctionBlockKey,
/// The initial stack state hash.
pub stack_hash: md5::Digest,
}
impl VisitedElement {
/// A shortcut constructor.
pub fn new(
block_key: revive_llvm_context::PolkaVMFunctionBlockKey,
stack_hash: md5::Digest,
) -> Self {
Self {
block_key,
stack_hash,
}
}
}
impl PartialOrd for VisitedElement {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for VisitedElement {
fn cmp(&self, other: &Self) -> Ordering {
match (self.block_key.code_type, other.block_key.code_type) {
(
revive_llvm_context::PolkaVMCodeType::Deploy,
revive_llvm_context::PolkaVMCodeType::Runtime,
) => Ordering::Less,
(
revive_llvm_context::PolkaVMCodeType::Runtime,
revive_llvm_context::PolkaVMCodeType::Deploy,
) => Ordering::Greater,
(
revive_llvm_context::PolkaVMCodeType::Deploy,
revive_llvm_context::PolkaVMCodeType::Deploy,
)
| (
revive_llvm_context::PolkaVMCodeType::Runtime,
revive_llvm_context::PolkaVMCodeType::Runtime,
) => {
let tag_comparison = self.block_key.tag.cmp(&other.block_key.tag);
if tag_comparison == Ordering::Equal {
if self.stack_hash == other.stack_hash {
Ordering::Equal
} else {
Ordering::Less
}
} else {
tag_comparison
}
}
}
}
}
@@ -1,134 +0,0 @@
//! The Ethereal IR of the EVM bytecode.
pub mod entry_link;
pub mod function;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::collections::HashMap;
use crate::evmla::assembly::instruction::Instruction;
use crate::solc::standard_json::output::contract::evm::extra_metadata::ExtraMetadata;
use self::function::block::Block;
use self::function::r#type::Type as FunctionType;
use self::function::Function;
/// The Ethereal IR of the EVM bytecode.
/// The Ethereal IR (EthIR) is a special IR between the EVM legacy assembly and LLVM IR. It is
/// created to facilitate the translation and provide an additional environment for applying some
/// transformations, duplicating parts of the call and control flow graphs, tracking the
/// data flow, and a few more algorithms of static analysis.
/// The most important feature of EthIR is flattening the block tags and duplicating blocks for
/// each of initial states of the stack. The LLVM IR supports only static control flow, so the
/// stack state must be known all the way throughout the program.
#[derive(Debug)]
pub struct EtherealIR {
/// The Solidity compiler version.
pub solc_version: semver::Version,
/// The EVMLA extra metadata.
pub extra_metadata: ExtraMetadata,
/// The all-inlined function.
pub entry_function: Function,
/// The recursive functions.
pub recursive_functions: BTreeMap<revive_llvm_context::PolkaVMFunctionBlockKey, Function>,
}
impl EtherealIR {
/// The default entry function name.
pub const DEFAULT_ENTRY_FUNCTION_NAME: &'static str = "main";
/// The blocks hashmap initial capacity.
pub const BLOCKS_HASHMAP_DEFAULT_CAPACITY: usize = 64;
/// Assembles a sequence of functions from the sequence of instructions.
pub fn new(
solc_version: semver::Version,
extra_metadata: ExtraMetadata,
blocks: HashMap<revive_llvm_context::PolkaVMFunctionBlockKey, Block>,
) -> anyhow::Result<Self> {
let mut entry_function = Function::new(solc_version.clone(), FunctionType::new_initial());
let mut recursive_functions = BTreeMap::new();
let mut visited_functions = BTreeSet::new();
entry_function.traverse(
&blocks,
&mut recursive_functions,
&extra_metadata,
&mut visited_functions,
)?;
Ok(Self {
solc_version,
extra_metadata,
entry_function,
recursive_functions,
})
}
/// Gets blocks for the specified type of the contract code.
pub fn get_blocks(
solc_version: semver::Version,
code_type: revive_llvm_context::PolkaVMCodeType,
instructions: &[Instruction],
) -> anyhow::Result<HashMap<revive_llvm_context::PolkaVMFunctionBlockKey, Block>> {
let mut blocks = HashMap::with_capacity(Self::BLOCKS_HASHMAP_DEFAULT_CAPACITY);
let mut offset = 0;
while offset < instructions.len() {
let (block, size) = Block::try_from_instructions(
solc_version.clone(),
code_type,
&instructions[offset..],
)?;
blocks.insert(
revive_llvm_context::PolkaVMFunctionBlockKey::new(code_type, block.key.tag.clone()),
block,
);
offset += size;
}
Ok(blocks)
}
}
impl<D> revive_llvm_context::PolkaVMWriteLLVM<D> for EtherealIR
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
fn declare(
&mut self,
context: &mut revive_llvm_context::PolkaVMContext<D>,
) -> anyhow::Result<()> {
self.entry_function.declare(context)?;
for (_key, function) in self.recursive_functions.iter_mut() {
function.declare(context)?;
}
Ok(())
}
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> {
context.evmla_mut().stack = vec![];
self.entry_function.into_llvm(context)?;
for (_key, function) in self.recursive_functions.into_iter() {
function.into_llvm(context)?;
}
Ok(())
}
}
impl std::fmt::Display for EtherealIR {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.entry_function)?;
for (_key, function) in self.recursive_functions.iter() {
writeln!(f, "{}", function)?;
}
Ok(())
}
}
-4
View File
@@ -1,4 +0,0 @@
//! The EVM legacy assembly compiling tools.
pub mod assembly;
pub mod ethereal_ir;
+13 -43
View File
@@ -2,7 +2,6 @@
pub(crate) mod build; pub(crate) mod build;
pub(crate) mod r#const; pub(crate) mod r#const;
pub(crate) mod evmla;
pub(crate) mod missing_libraries; pub(crate) mod missing_libraries;
pub(crate) mod process; pub(crate) mod process;
pub(crate) mod project; pub(crate) mod project;
@@ -26,7 +25,6 @@ pub use self::project::Project;
pub use self::r#const::*; pub use self::r#const::*;
pub use self::solc::combined_json::contract::Contract as SolcCombinedJsonContract; pub use self::solc::combined_json::contract::Contract as SolcCombinedJsonContract;
pub use self::solc::combined_json::CombinedJson as SolcCombinedJson; pub use self::solc::combined_json::CombinedJson as SolcCombinedJson;
pub use self::solc::pipeline::Pipeline as SolcPipeline;
#[cfg(not(target_os = "emscripten"))] #[cfg(not(target_os = "emscripten"))]
pub use self::solc::solc_compiler::SolcCompiler; pub use self::solc::solc_compiler::SolcCompiler;
#[cfg(target_os = "emscripten")] #[cfg(target_os = "emscripten")]
@@ -53,6 +51,7 @@ pub mod test_utils;
pub mod tests; pub mod tests;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
/// Runs the Yul mode. /// Runs the Yul mode.
@@ -119,7 +118,6 @@ pub fn standard_output<T: Compiler>(
evm_version: Option<revive_common::EVMVersion>, evm_version: Option<revive_common::EVMVersion>,
solc_optimizer_enabled: bool, solc_optimizer_enabled: bool,
optimizer_settings: revive_llvm_context::OptimizerSettings, optimizer_settings: revive_llvm_context::OptimizerSettings,
force_evmla: bool,
include_metadata_hash: bool, include_metadata_hash: bool,
base_path: Option<String>, base_path: Option<String>,
include_paths: Vec<String>, include_paths: Vec<String>,
@@ -129,7 +127,6 @@ pub fn standard_output<T: Compiler>(
debug_config: revive_llvm_context::DebugConfig, debug_config: revive_llvm_context::DebugConfig,
) -> anyhow::Result<Build> { ) -> anyhow::Result<Build> {
let solc_version = solc.version()?; let solc_version = solc.version()?;
let solc_pipeline = SolcPipeline::new(&solc_version, force_evmla);
let solc_input = SolcStandardJsonInput::try_from_paths( let solc_input = SolcStandardJsonInput::try_from_paths(
SolcStandardJsonInputLanguage::Solidity, SolcStandardJsonInputLanguage::Solidity,
@@ -137,7 +134,7 @@ pub fn standard_output<T: Compiler>(
input_files, input_files,
libraries, libraries,
remappings, remappings,
SolcStandardJsonInputSettingsSelection::new_required(solc_pipeline), SolcStandardJsonInputSettingsSelection::new_required(),
SolcStandardJsonInputSettingsOptimizer::new( SolcStandardJsonInputSettingsOptimizer::new(
solc_optimizer_enabled, solc_optimizer_enabled,
None, None,
@@ -145,7 +142,6 @@ pub fn standard_output<T: Compiler>(
optimizer_settings.is_fallback_to_size_enabled(), optimizer_settings.is_fallback_to_size_enabled(),
), ),
None, None,
solc_pipeline == SolcPipeline::Yul,
suppressed_warnings, suppressed_warnings,
)?; )?;
@@ -156,13 +152,7 @@ pub fn standard_output<T: Compiler>(
.collect(); .collect();
let libraries = solc_input.settings.libraries.clone().unwrap_or_default(); let libraries = solc_input.settings.libraries.clone().unwrap_or_default();
let mut solc_output = solc.standard_json( let mut solc_output = solc.standard_json(solc_input, base_path, include_paths, allow_paths)?;
solc_input,
solc_pipeline,
base_path,
include_paths,
allow_paths,
)?;
if let Some(errors) = solc_output.errors.as_deref() { if let Some(errors) = solc_output.errors.as_deref() {
let mut has_errors = false; let mut has_errors = false;
@@ -172,7 +162,7 @@ pub fn standard_output<T: Compiler>(
has_errors = true; has_errors = true;
} }
eprintln!("{error}"); writeln!(std::io::stderr(), "{error}")?;
} }
if has_errors { if has_errors {
@@ -180,13 +170,8 @@ pub fn standard_output<T: Compiler>(
} }
} }
let project = solc_output.try_to_project( let project =
source_code_files, solc_output.try_to_project(source_code_files, libraries, &solc_version, &debug_config)?;
libraries,
solc_pipeline,
&solc_version,
&debug_config,
)?;
let build = project.compile(optimizer_settings, include_metadata_hash, debug_config)?; let build = project.compile(optimizer_settings, include_metadata_hash, debug_config)?;
@@ -194,20 +179,17 @@ pub fn standard_output<T: Compiler>(
} }
/// Runs the standard JSON mode. /// Runs the standard JSON mode.
#[allow(clippy::too_many_arguments)]
pub fn standard_json<T: Compiler>( pub fn standard_json<T: Compiler>(
solc: &mut T, solc: &mut T,
detect_missing_libraries: bool, detect_missing_libraries: bool,
force_evmla: bool,
base_path: Option<String>, base_path: Option<String>,
include_paths: Vec<String>, include_paths: Vec<String>,
allow_paths: Option<String>, allow_paths: Option<String>,
debug_config: revive_llvm_context::DebugConfig, debug_config: revive_llvm_context::DebugConfig,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let solc_version = solc.version()?; let solc_version = solc.version()?;
let solc_pipeline = SolcPipeline::new(&solc_version, force_evmla);
let solc_input = SolcStandardJsonInput::try_from_stdin(solc_pipeline)?; let solc_input = SolcStandardJsonInput::try_from_stdin()?;
let source_code_files = solc_input let source_code_files = solc_input
.sources .sources
.iter() .iter()
@@ -225,13 +207,7 @@ pub fn standard_json<T: Compiler>(
}; };
let libraries = solc_input.settings.libraries.clone().unwrap_or_default(); let libraries = solc_input.settings.libraries.clone().unwrap_or_default();
let mut solc_output = solc.standard_json( let mut solc_output = solc.standard_json(solc_input, base_path, include_paths, allow_paths)?;
solc_input,
solc_pipeline,
base_path,
include_paths,
allow_paths,
)?;
if let Some(errors) = solc_output.errors.as_deref() { if let Some(errors) = solc_output.errors.as_deref() {
for error in errors.iter() { for error in errors.iter() {
@@ -242,13 +218,8 @@ pub fn standard_json<T: Compiler>(
} }
} }
let project = solc_output.try_to_project( let project =
source_code_files, solc_output.try_to_project(source_code_files, libraries, &solc_version, &debug_config)?;
libraries,
solc_pipeline,
&solc_version,
&debug_config,
)?;
if detect_missing_libraries { if detect_missing_libraries {
let missing_libraries = project.get_missing_libraries(); let missing_libraries = project.get_missing_libraries();
@@ -271,7 +242,6 @@ pub fn combined_json<T: Compiler>(
evm_version: Option<revive_common::EVMVersion>, evm_version: Option<revive_common::EVMVersion>,
solc_optimizer_enabled: bool, solc_optimizer_enabled: bool,
optimizer_settings: revive_llvm_context::OptimizerSettings, optimizer_settings: revive_llvm_context::OptimizerSettings,
force_evmla: bool,
include_metadata_hash: bool, include_metadata_hash: bool,
base_path: Option<String>, base_path: Option<String>,
include_paths: Vec<String>, include_paths: Vec<String>,
@@ -289,7 +259,6 @@ pub fn combined_json<T: Compiler>(
evm_version, evm_version,
solc_optimizer_enabled, solc_optimizer_enabled,
optimizer_settings, optimizer_settings,
force_evmla,
include_metadata_hash, include_metadata_hash,
base_path, base_path,
include_paths, include_paths,
@@ -309,10 +278,11 @@ pub fn combined_json<T: Compiler>(
combined_json.write_to_directory(output_directory.as_path(), overwrite)?; combined_json.write_to_directory(output_directory.as_path(), overwrite)?;
} }
None => { None => {
println!( writeln!(
std::io::stdout(),
"{}", "{}",
serde_json::to_string(&combined_json).expect("Always valid") serde_json::to_string(&combined_json).expect("Always valid")
); )?;
} }
} }
std::process::exit(0); std::process::exit(0);
@@ -1,47 +0,0 @@
//! The contract EVM legacy assembly source code.
use std::collections::HashSet;
use serde::Deserialize;
use serde::Serialize;
use crate::evmla::assembly::Assembly;
use crate::solc::standard_json::output::contract::evm::extra_metadata::ExtraMetadata;
/// The contract EVM legacy assembly source code.
#[derive(Debug, Serialize, Deserialize, Clone)]
#[allow(non_camel_case_types)]
#[allow(clippy::upper_case_acronyms)]
pub struct EVMLA {
/// The EVM legacy assembly source code.
pub assembly: Assembly,
}
impl EVMLA {
/// A shortcut constructor.
pub fn new(mut assembly: Assembly, extra_metadata: ExtraMetadata) -> Self {
assembly.extra_metadata = Some(extra_metadata);
Self { assembly }
}
/// Get the list of missing deployable libraries.
pub fn get_missing_libraries(&self) -> HashSet<String> {
self.assembly.get_missing_libraries()
}
}
impl<D> revive_llvm_context::PolkaVMWriteLLVM<D> for EVMLA
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
fn declare(
&mut self,
context: &mut revive_llvm_context::PolkaVMContext<D>,
) -> anyhow::Result<()> {
self.assembly.declare(context)
}
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> {
self.assembly.into_llvm(context)
}
}
@@ -1,6 +1,5 @@
//! The contract source code. //! The contract source code.
pub mod evmla;
pub mod llvm_ir; pub mod llvm_ir;
pub mod yul; pub mod yul;
@@ -9,24 +8,17 @@ use std::collections::HashSet;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use crate::evmla::assembly::Assembly;
use crate::solc::standard_json::output::contract::evm::extra_metadata::ExtraMetadata;
use crate::yul::parser::statement::object::Object; use crate::yul::parser::statement::object::Object;
use self::evmla::EVMLA;
use self::llvm_ir::LLVMIR; use self::llvm_ir::LLVMIR;
use self::yul::Yul; use self::yul::Yul;
/// The contract source code. /// The contract source code.
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
#[allow(non_camel_case_types)]
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
#[allow(clippy::enum_variant_names)]
pub enum IR { pub enum IR {
/// The Yul source code. /// The Yul source code.
Yul(Yul), Yul(Yul),
/// The EVM legacy assembly source code.
EVMLA(EVMLA),
/// The LLVM IR source code. /// The LLVM IR source code.
LLVMIR(LLVMIR), LLVMIR(LLVMIR),
} }
@@ -37,11 +29,6 @@ impl IR {
Self::Yul(Yul::new(source_code, object)) Self::Yul(Yul::new(source_code, object))
} }
/// A shortcut constructor.
pub fn new_evmla(assembly: Assembly, extra_metadata: ExtraMetadata) -> Self {
Self::EVMLA(EVMLA::new(assembly, extra_metadata))
}
/// A shortcut constructor. /// A shortcut constructor.
pub fn new_llvm_ir(path: String, source: String) -> Self { pub fn new_llvm_ir(path: String, source: String) -> Self {
Self::LLVMIR(LLVMIR::new(path, source)) Self::LLVMIR(LLVMIR::new(path, source))
@@ -51,7 +38,6 @@ impl IR {
pub fn get_missing_libraries(&self) -> HashSet<String> { pub fn get_missing_libraries(&self) -> HashSet<String> {
match self { match self {
Self::Yul(inner) => inner.get_missing_libraries(), Self::Yul(inner) => inner.get_missing_libraries(),
Self::EVMLA(inner) => inner.get_missing_libraries(),
Self::LLVMIR(_inner) => HashSet::new(), Self::LLVMIR(_inner) => HashSet::new(),
} }
} }
@@ -67,7 +53,6 @@ where
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
match self { match self {
Self::Yul(inner) => inner.declare(context), Self::Yul(inner) => inner.declare(context),
Self::EVMLA(inner) => inner.declare(context),
Self::LLVMIR(_inner) => Ok(()), Self::LLVMIR(_inner) => Ok(()),
} }
} }
@@ -75,7 +60,6 @@ where
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> { fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> {
match self { match self {
Self::Yul(inner) => inner.into_llvm(context), Self::Yul(inner) => inner.into_llvm(context),
Self::EVMLA(inner) => inner.into_llvm(context),
Self::LLVMIR(_inner) => Ok(()), Self::LLVMIR(_inner) => Ok(()),
} }
} }
@@ -54,12 +54,10 @@ impl Contract {
/// Returns the contract identifier, which is: /// Returns the contract identifier, which is:
/// - the Yul object identifier for Yul /// - the Yul object identifier for Yul
/// - the full contract path for EVM legacy assembly
/// - the module name for LLVM IR /// - the module name for LLVM IR
pub fn identifier(&self) -> &str { pub fn identifier(&self) -> &str {
match self.ir { match self.ir {
IR::Yul(ref yul) => yul.object.identifier.as_str(), IR::Yul(ref yul) => yul.object.identifier.as_str(),
IR::EVMLA(ref evm) => evm.assembly.full_path(),
IR::LLVMIR(ref llvm_ir) => llvm_ir.path.as_str(), IR::LLVMIR(ref llvm_ir) => llvm_ir.path.as_str(),
} }
} }
@@ -68,7 +66,6 @@ impl Contract {
pub fn drain_factory_dependencies(&mut self) -> HashSet<String> { pub fn drain_factory_dependencies(&mut self) -> HashSet<String> {
match self.ir { match self.ir {
IR::Yul(ref mut yul) => yul.object.factory_dependencies.drain().collect(), IR::Yul(ref mut yul) => yul.object.factory_dependencies.drain().collect(),
IR::EVMLA(ref mut evm) => evm.assembly.factory_dependencies.drain().collect(),
IR::LLVMIR(_) => HashSet::new(), IR::LLVMIR(_) => HashSet::new(),
} }
} }
@@ -129,10 +126,6 @@ impl Contract {
IR::Yul(_) => { IR::Yul(_) => {
context.set_yul_data(Default::default()); context.set_yul_data(Default::default());
} }
IR::EVMLA(_) => {
let evmla_data = revive_llvm_context::PolkaVMContextEVMLAData::new(version.default);
context.set_evmla_data(evmla_data);
}
IR::LLVMIR(_) => {} IR::LLVMIR(_) => {}
} }
+31 -57
View File
@@ -13,14 +13,14 @@ use path_slash::PathExt;
/// output directory. /// output directory.
/// Example: resolc ERC20.sol -O3 --bin --output-dir './build/' /// Example: resolc ERC20.sol -O3 --bin --output-dir './build/'
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
#[structopt(name = "The PolkaVM Solidity compiler")] #[command(name = "The PolkaVM Solidity compiler", arg_required_else_help = true)]
pub struct Arguments { pub struct Arguments {
/// Print the version and exit. /// Print the version and exit.
#[structopt(long = "version")] #[arg(long = "version")]
pub version: bool, pub version: bool,
/// Print the licence and exit. /// Print the licence and exit.
#[structopt(long = "license")] #[arg(long = "license")]
pub license: bool, pub license: bool,
/// Specify the input paths and remappings. /// Specify the input paths and remappings.
@@ -31,159 +31,141 @@ pub struct Arguments {
/// Set the given path as the root of the source tree instead of the root of the filesystem. /// Set the given path as the root of the source tree instead of the root of the filesystem.
/// Passed to `solc` without changes. /// Passed to `solc` without changes.
#[structopt(long = "base-path")] #[arg(long = "base-path")]
pub base_path: Option<String>, pub base_path: Option<String>,
/// Make an additional source directory available to the default import callback. /// Make an additional source directory available to the default import callback.
/// Can be used multiple times. Can only be used if the base path has a non-empty value. /// Can be used multiple times. Can only be used if the base path has a non-empty value.
/// Passed to `solc` without changes. /// Passed to `solc` without changes.
#[structopt(long = "include-path")] #[arg(long = "include-path")]
pub include_paths: Vec<String>, pub include_paths: Vec<String>,
/// Allow a given path for imports. A list of paths can be supplied by separating them with a comma. /// Allow a given path for imports. A list of paths can be supplied by separating them with a comma.
/// Passed to `solc` without changes. /// Passed to `solc` without changes.
#[structopt(long = "allow-paths")] #[arg(long = "allow-paths")]
pub allow_paths: Option<String>, pub allow_paths: Option<String>,
/// Create one file per component and contract/file at the specified directory, if given. /// Create one file per component and contract/file at the specified directory, if given.
#[structopt(short = 'o', long = "output-dir")] #[arg(short = 'o', long = "output-dir")]
pub output_directory: Option<PathBuf>, pub output_directory: Option<PathBuf>,
/// Overwrite existing files (used together with -o). /// Overwrite existing files (used together with -o).
#[structopt(long = "overwrite")] #[arg(long = "overwrite")]
pub overwrite: bool, pub overwrite: bool,
/// Set the optimization parameter -O[0 | 1 | 2 | 3 | s | z]. /// Set the optimization parameter -O[0 | 1 | 2 | 3 | s | z].
/// Use `3` for best performance and `z` for minimal size. /// Use `3` for best performance and `z` for minimal size.
#[structopt(short = 'O', long = "optimization")] #[arg(short = 'O', long = "optimization")]
pub optimization: Option<char>, pub optimization: Option<char>,
/// Try to recompile with -Oz if the bytecode is too large. /// Try to recompile with -Oz if the bytecode is too large.
#[structopt(long = "fallback-Oz")] #[arg(long = "fallback-Oz")]
pub fallback_to_optimizing_for_size: bool, pub fallback_to_optimizing_for_size: bool,
/// Disable the `solc` optimizer. /// Disable the `solc` optimizer.
/// Use it if your project uses the `MSIZE` instruction, or in other cases. /// Use it if your project uses the `MSIZE` instruction, or in other cases.
/// Beware that it will prevent libraries from being inlined. /// Beware that it will prevent libraries from being inlined.
#[structopt(long = "disable-solc-optimizer")] #[arg(long = "disable-solc-optimizer")]
pub disable_solc_optimizer: bool, pub disable_solc_optimizer: bool,
/// Specify the path to the `solc` executable. By default, the one in `${PATH}` is used. /// Specify the path to the `solc` executable. By default, the one in `${PATH}` is used.
/// Yul mode: `solc` is used for source code validation, as `resolc` itself assumes that the input Yul is valid. /// Yul mode: `solc` is used for source code validation, as `resolc` itself assumes that the input Yul is valid.
/// LLVM IR mode: `solc` is unused. /// LLVM IR mode: `solc` is unused.
#[structopt(long = "solc")] #[arg(long = "solc")]
pub solc: Option<String>, pub solc: Option<String>,
/// The EVM target version to generate IR for. /// The EVM target version to generate IR for.
/// See https://github.com/paritytech/revive/blob/main/crates/common/src/evm_version.rs for reference. /// See https://github.com/paritytech/revive/blob/main/crates/common/src/evm_version.rs for reference.
#[structopt(long = "evm-version")] #[arg(long = "evm-version")]
pub evm_version: Option<String>, pub evm_version: Option<String>,
/// Specify addresses of deployable libraries. Syntax: `<libraryName>=<address> [, or whitespace] ...`. /// Specify addresses of deployable libraries. Syntax: `<libraryName>=<address> [, or whitespace] ...`.
/// Addresses are interpreted as hexadecimal strings prefixed with `0x`. /// Addresses are interpreted as hexadecimal strings prefixed with `0x`.
#[structopt(short = 'l', long = "libraries")] #[arg(short = 'l', long = "libraries")]
pub libraries: Vec<String>, pub libraries: Vec<String>,
/// Output a single JSON document containing the specified information. /// Output a single JSON document containing the specified information.
/// Available arguments: `abi`, `hashes`, `metadata`, `devdoc`, `userdoc`, `storage-layout`, `ast`, `asm`, `bin`, `bin-runtime`. /// Available arguments: `abi`, `hashes`, `metadata`, `devdoc`, `userdoc`, `storage-layout`, `ast`, `asm`, `bin`, `bin-runtime`.
#[structopt(long = "combined-json")] #[arg(long = "combined-json")]
pub combined_json: Option<String>, pub combined_json: Option<String>,
/// Switch to standard JSON input/output mode. Read from stdin, write the result to stdout. /// Switch to standard JSON input/output mode. Read from stdin, write the result to stdout.
/// This is the default used by the Hardhat plugin. /// This is the default used by the Hardhat plugin.
#[structopt(long = "standard-json")] #[arg(long = "standard-json")]
pub standard_json: bool, pub standard_json: bool,
/// Switch to missing deployable libraries detection mode. /// Switch to missing deployable libraries detection mode.
/// Only available for standard JSON input/output mode. /// Only available for standard JSON input/output mode.
/// Contracts are not compiled in this mode, and all compilation artifacts are not included. /// Contracts are not compiled in this mode, and all compilation artifacts are not included.
#[structopt(long = "detect-missing-libraries")] #[arg(long = "detect-missing-libraries")]
pub detect_missing_libraries: bool, pub detect_missing_libraries: bool,
/// Switch to Yul mode. /// Switch to Yul mode.
/// Only one input Yul file is allowed. /// Only one input Yul file is allowed.
/// Cannot be used with combined and standard JSON modes. /// Cannot be used with combined and standard JSON modes.
#[structopt(long = "yul")] #[arg(long = "yul")]
pub yul: bool, pub yul: bool,
/// Switch to LLVM IR mode. /// Switch to LLVM IR mode.
/// Only one input LLVM IR file is allowed. /// Only one input LLVM IR file is allowed.
/// Cannot be used with combined and standard JSON modes. /// Cannot be used with combined and standard JSON modes.
/// Use this mode at your own risk, as LLVM IR input validation is not implemented. /// Use this mode at your own risk, as LLVM IR input validation is not implemented.
#[structopt(long = "llvm-ir")] #[arg(long = "llvm-ir")]
pub llvm_ir: bool, pub llvm_ir: bool,
/// Forcibly switch to EVM legacy assembly pipeline.
/// It is useful for older revisions of `solc` 0.8, where Yul was considered highly experimental
/// and contained more bugs than today.
#[structopt(long = "force-evmla")]
pub force_evmla: bool,
/// Set metadata hash mode. /// Set metadata hash mode.
/// The only supported value is `none` that disables appending the metadata hash. /// The only supported value is `none` that disables appending the metadata hash.
/// Is enabled by default. /// Is enabled by default.
#[structopt(long = "metadata-hash")] #[arg(long = "metadata-hash")]
pub metadata_hash: Option<String>, pub metadata_hash: Option<String>,
/// Output PolkaVM assembly of the contracts. /// Output PolkaVM assembly of the contracts.
#[structopt(long = "asm")] #[arg(long = "asm")]
pub output_assembly: bool, pub output_assembly: bool,
/// Output PolkaVM bytecode of the contracts. /// Output PolkaVM bytecode of the contracts.
#[structopt(long = "bin")] #[arg(long = "bin")]
pub output_binary: bool, pub output_binary: bool,
/// Suppress specified warnings. /// Suppress specified warnings.
/// Available arguments: `ecrecover`, `sendtransfer`, `extcodesize`, `txorigin`, `blocktimestamp`, `blocknumber`, `blockhash`. /// Available arguments: `ecrecover`, `sendtransfer`, `extcodesize`, `txorigin`, `blocktimestamp`, `blocknumber`, `blockhash`.
#[structopt(long = "suppress-warnings")] #[arg(long = "suppress-warnings")]
pub suppress_warnings: Option<Vec<String>>, pub suppress_warnings: Option<Vec<String>>,
/// Generate source based debug information in the output code file. This only has an effect /// Generate source based debug information in the output code file. This only has an effect
/// with the LLVM-IR code generator and is ignored otherwise. /// with the LLVM-IR code generator and is ignored otherwise.
#[structopt(short = 'g')] #[arg(short = 'g')]
pub emit_source_debug_info: bool, pub emit_source_debug_info: bool,
/// Dump all IRs to files in the specified directory. /// Dump all IRs to files in the specified directory.
/// Only for testing and debugging. /// Only for testing and debugging.
#[structopt(long = "debug-output-dir")] #[arg(long = "debug-output-dir")]
pub debug_output_directory: Option<PathBuf>, pub debug_output_directory: Option<PathBuf>,
/// Set the verify-each option in LLVM. /// Set the verify-each option in LLVM.
/// Only for testing and debugging. /// Only for testing and debugging.
#[structopt(long = "llvm-verify-each")] #[arg(long = "llvm-verify-each")]
pub llvm_verify_each: bool, pub llvm_verify_each: bool,
/// Set the debug-logging option in LLVM. /// Set the debug-logging option in LLVM.
/// Only for testing and debugging. /// Only for testing and debugging.
#[structopt(long = "llvm-debug-logging")] #[arg(long = "llvm-debug-logging")]
pub llvm_debug_logging: bool, pub llvm_debug_logging: bool,
/// Run this process recursively and provide JSON input to compile a single contract. /// Run this process recursively and provide JSON input to compile a single contract.
/// Only for usage from within the compiler. /// Only for usage from within the compiler.
#[structopt(long = "recursive-process")] #[arg(long = "recursive-process")]
pub recursive_process: bool, pub recursive_process: bool,
/// Specify the input file to use instead of stdin when --recursive-process is given. /// Specify the input file to use instead of stdin when --recursive-process is given.
/// This is only intended for use when developing the compiler. /// This is only intended for use when developing the compiler.
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
#[structopt(long = "recursive-process-input")] #[arg(long = "recursive-process-input")]
pub recursive_process_input: Option<String>, pub recursive_process_input: Option<String>,
} }
impl Default for Arguments {
fn default() -> Self {
Self::new()
}
}
impl Arguments { impl Arguments {
/// A shortcut constructor.
pub fn new() -> Self {
Self::parse()
}
/// Validates the arguments. /// Validates the arguments.
#[allow(clippy::collapsible_if)]
pub fn validate(&self) -> anyhow::Result<()> { pub fn validate(&self) -> anyhow::Result<()> {
if self.version && std::env::args().count() > 2 { if self.version && std::env::args().count() > 2 {
anyhow::bail!("No other options are allowed while getting the compiler version."); anyhow::bail!("No other options are allowed while getting the compiler version.");
@@ -248,28 +230,20 @@ impl Arguments {
); );
} }
if self.force_evmla {
anyhow::bail!("EVM legacy assembly mode is not supported in Yul, LLVM IR and PolkaVM assembly modes.");
}
if self.disable_solc_optimizer { if self.disable_solc_optimizer {
anyhow::bail!("Disabling the solc optimizer is not supported in Yul, LLVM IR and PolkaVM assembly modes."); anyhow::bail!("Disabling the solc optimizer is not supported in Yul, LLVM IR and PolkaVM assembly modes.");
} }
} }
if self.llvm_ir { if self.llvm_ir && self.solc.is_some() {
if self.solc.is_some() {
anyhow::bail!("`solc` is not used in LLVM IR and PolkaVM assembly modes."); anyhow::bail!("`solc` is not used in LLVM IR and PolkaVM assembly modes.");
} }
}
if self.combined_json.is_some() { if self.combined_json.is_some() && (self.output_assembly || self.output_binary) {
if self.output_assembly || self.output_binary {
anyhow::bail!( anyhow::bail!(
"Cannot output assembly or binary outside of JSON in combined JSON mode." "Cannot output assembly or binary outside of JSON in combined JSON mode."
); );
} }
}
if self.standard_json { if self.standard_json {
if self.output_assembly || self.output_binary { if self.output_assembly || self.output_binary {
+25 -18
View File
@@ -2,6 +2,7 @@
pub mod arguments; pub mod arguments;
use std::io::Write;
use std::str::FromStr; use std::str::FromStr;
use revive_solidity::Process; use revive_solidity::Process;
@@ -16,28 +17,27 @@ const RAYON_WORKER_STACK_SIZE: usize = 16 * 1024 * 1024;
#[global_allocator] #[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
/// The application entry point. fn main() -> anyhow::Result<()> {
fn main() {
std::process::exit(match main_inner() { std::process::exit(match main_inner() {
Ok(()) => revive_common::EXIT_CODE_SUCCESS, Ok(()) => revive_common::EXIT_CODE_SUCCESS,
Err(error) => { Err(error) => {
eprintln!("{error}"); writeln!(std::io::stderr(), "{error}")?;
revive_common::EXIT_CODE_FAILURE revive_common::EXIT_CODE_FAILURE
} }
}) })
} }
/// The auxiliary `main` function to facilitate the `?` error conversion operator.
fn main_inner() -> anyhow::Result<()> { fn main_inner() -> anyhow::Result<()> {
let arguments = Arguments::new(); let arguments = <Arguments as clap::Parser>::try_parse()?;
arguments.validate()?; arguments.validate()?;
if arguments.version { if arguments.version {
println!( writeln!(
std::io::stdout(),
"{} version {}", "{} version {}",
env!("CARGO_PKG_DESCRIPTION"), env!("CARGO_PKG_DESCRIPTION"),
revive_solidity::ResolcVersion::default().long revive_solidity::ResolcVersion::default().long
); )?;
return Ok(()); return Ok(());
} }
@@ -45,7 +45,7 @@ fn main_inner() -> anyhow::Result<()> {
let license_mit = include_str!("../../../../LICENSE-MIT"); let license_mit = include_str!("../../../../LICENSE-MIT");
let license_apache = include_str!("../../../../LICENSE-APACHE"); let license_apache = include_str!("../../../../LICENSE-APACHE");
println!("{}\n{}\n", license_mit, license_apache); writeln!(std::io::stdout(), "{}\n{}\n", license_mit, license_apache)?;
return Ok(()); return Ok(());
} }
@@ -103,7 +103,7 @@ fn main_inner() -> anyhow::Result<()> {
let mut solc = { let mut solc = {
#[cfg(target_os = "emscripten")] #[cfg(target_os = "emscripten")]
{ {
revive_solidity::SoljsonCompiler { version: None } revive_solidity::SoljsonCompiler
} }
#[cfg(not(target_os = "emscripten"))] #[cfg(not(target_os = "emscripten"))]
@@ -157,7 +157,6 @@ fn main_inner() -> anyhow::Result<()> {
revive_solidity::standard_json( revive_solidity::standard_json(
&mut solc, &mut solc,
arguments.detect_missing_libraries, arguments.detect_missing_libraries,
arguments.force_evmla,
arguments.base_path, arguments.base_path,
arguments.include_paths, arguments.include_paths,
arguments.allow_paths, arguments.allow_paths,
@@ -173,7 +172,6 @@ fn main_inner() -> anyhow::Result<()> {
evm_version, evm_version,
!arguments.disable_solc_optimizer, !arguments.disable_solc_optimizer,
optimizer_settings, optimizer_settings,
arguments.force_evmla,
include_metadata_hash, include_metadata_hash,
arguments.base_path, arguments.base_path,
arguments.include_paths, arguments.include_paths,
@@ -193,7 +191,6 @@ fn main_inner() -> anyhow::Result<()> {
evm_version, evm_version,
!arguments.disable_solc_optimizer, !arguments.disable_solc_optimizer,
optimizer_settings, optimizer_settings,
arguments.force_evmla,
include_metadata_hash, include_metadata_hash,
arguments.base_path, arguments.base_path,
arguments.include_paths, arguments.include_paths,
@@ -214,26 +211,36 @@ fn main_inner() -> anyhow::Result<()> {
arguments.overwrite, arguments.overwrite,
)?; )?;
eprintln!( writeln!(
std::io::stderr(),
"Compiler run successful. Artifact(s) can be found in directory {output_directory:?}." "Compiler run successful. Artifact(s) can be found in directory {output_directory:?}."
); )?;
} else if arguments.output_assembly || arguments.output_binary { } else if arguments.output_assembly || arguments.output_binary {
for (path, contract) in build.contracts.into_iter() { for (path, contract) in build.contracts.into_iter() {
if arguments.output_assembly { if arguments.output_assembly {
let assembly_text = contract.build.assembly_text; let assembly_text = contract.build.assembly_text;
println!("Contract `{}` assembly:\n\n{}", path, assembly_text); writeln!(
std::io::stdout(),
"Contract `{}` assembly:\n\n{}",
path,
assembly_text
)?;
} }
if arguments.output_binary { if arguments.output_binary {
println!( writeln!(
std::io::stdout(),
"Contract `{}` bytecode: 0x{}", "Contract `{}` bytecode: 0x{}",
path, path,
hex::encode(contract.build.bytecode) hex::encode(contract.build.bytecode)
); )?;
} }
} }
} else { } else {
eprintln!("Compiler run successful. No output requested. Use --asm and --bin flags."); writeln!(
std::io::stderr(),
"Compiler run successful. No output requested. Use --asm and --bin flags."
)?;
} }
Ok(()) Ok(())
@@ -80,10 +80,9 @@ impl CombinedJson {
file_path.push(format!("combined.{}", revive_common::EXTENSION_JSON)); file_path.push(format!("combined.{}", revive_common::EXTENSION_JSON));
if file_path.exists() && !overwrite { if file_path.exists() && !overwrite {
eprintln!( anyhow::bail!(
"Refusing to overwrite an existing file {file_path:?} (use --overwrite to force)." "Refusing to overwrite an existing file {file_path:?} (use --overwrite to force)."
); );
return Ok(());
} }
File::create(&file_path) File::create(&file_path)
+3 -18
View File
@@ -1,7 +1,6 @@
//! The Solidity compiler. //! The Solidity compiler.
pub mod combined_json; pub mod combined_json;
pub mod pipeline;
#[cfg(not(target_os = "emscripten"))] #[cfg(not(target_os = "emscripten"))]
pub mod solc_compiler; pub mod solc_compiler;
#[cfg(target_os = "emscripten")] #[cfg(target_os = "emscripten")]
@@ -9,35 +8,22 @@ pub mod soljson_compiler;
pub mod standard_json; pub mod standard_json;
pub mod version; pub mod version;
use once_cell::sync::Lazy;
use semver::VersionReq;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use self::combined_json::CombinedJson; use self::combined_json::CombinedJson;
use self::pipeline::Pipeline;
use self::standard_json::input::Input as StandardJsonInput; use self::standard_json::input::Input as StandardJsonInput;
use self::standard_json::output::Output as StandardJsonOutput; use self::standard_json::output::Output as StandardJsonOutput;
use self::version::Version; use self::version::Version;
/// The first version of `solc` with the support of standard JSON interface. /// The first version of `solc` with the support of standard JSON interface.
pub const FIRST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 4, 12); pub const FIRST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 0);
/// The first version of `solc`, where Yul codegen is considered robust enough.
pub const FIRST_YUL_VERSION: semver::Version = semver::Version::new(0, 8, 0);
/// The first version of `solc`, where `--via-ir` codegen mode is supported.
pub const FIRST_VIA_IR_VERSION: semver::Version = semver::Version::new(0, 8, 13);
/// The last supported version of `solc`. /// The last supported version of `solc`.
pub const LAST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 28); pub const LAST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 28);
/// `--base-path` was introduced in 0.6.9 <https://github.com/ethereum/solidity/releases/tag/v0.6.9>
pub static FIRST_SUPPORTS_BASE_PATH: Lazy<VersionReq> =
Lazy::new(|| VersionReq::parse(">=0.6.9").unwrap());
/// `--include-path` was introduced in 0.8.8 <https://github.com/ethereum/solidity/releases/tag/v0.8.8> /// `--include-path` was introduced in solc `0.8.8` <https://github.com/ethereum/solidity/releases/tag/v0.8.8>
pub static FIRST_SUPPORTS_INCLUDE_PATH: Lazy<VersionReq> = pub const FIRST_INCLUDE_PATH_VERSION: semver::Version = semver::Version::new(0, 8, 8);
Lazy::new(|| VersionReq::parse(">=0.8.8").unwrap());
/// The Solidity compiler. /// The Solidity compiler.
pub trait Compiler { pub trait Compiler {
@@ -45,7 +31,6 @@ pub trait Compiler {
fn standard_json( fn standard_json(
&mut self, &mut self,
input: StandardJsonInput, input: StandardJsonInput,
pipeline: Pipeline,
base_path: Option<String>, base_path: Option<String>,
include_paths: Vec<String>, include_paths: Vec<String>,
allow_paths: Option<String>, allow_paths: Option<String>,
-27
View File
@@ -1,27 +0,0 @@
//! The Solidity compiler pipeline type.
use serde::{Deserialize, Serialize};
use crate::solc::version::Version as SolcVersion;
/// The Solidity compiler pipeline type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[allow(non_camel_case_types)]
#[allow(clippy::upper_case_acronyms)]
pub enum Pipeline {
/// The Yul IR.
Yul,
/// The EVM legacy assembly IR.
EVMLA,
}
impl Pipeline {
/// We always use EVMLA for Solidity <=0.7, or if the user does not want to compile via Yul.
pub fn new(solc_version: &SolcVersion, force_evmla: bool) -> Self {
if solc_version.default < crate::solc::FIRST_YUL_VERSION || force_evmla {
Self::EVMLA
} else {
Self::Yul
}
}
}
+20 -55
View File
@@ -1,24 +1,20 @@
//! The Solidity compiler. //! The Solidity compiler solc interface.
use std::io::Write; use std::io::Write;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use crate::solc::combined_json::CombinedJson; use crate::solc::combined_json::CombinedJson;
use crate::solc::pipeline::Pipeline;
use crate::solc::standard_json::input::Input as StandardJsonInput; use crate::solc::standard_json::input::Input as StandardJsonInput;
use crate::solc::standard_json::output::Output as StandardJsonOutput; use crate::solc::standard_json::output::Output as StandardJsonOutput;
use crate::solc::version::Version; use crate::solc::version::Version;
use super::Compiler; use super::Compiler;
use crate::solc::{FIRST_SUPPORTS_BASE_PATH, FIRST_SUPPORTS_INCLUDE_PATH};
/// The Solidity compiler. /// The Solidity compiler.
pub struct SolcCompiler { pub struct SolcCompiler {
/// The binary executable name. /// The binary executable name.
pub executable: String, pub executable: String,
/// The lazily-initialized compiler version.
pub version: Option<Version>,
} }
impl SolcCompiler { impl SolcCompiler {
@@ -35,10 +31,7 @@ impl SolcCompiler {
error error
); );
} }
Ok(Self { Ok(Self { executable })
executable,
version: None,
})
} }
} }
@@ -47,45 +40,31 @@ impl Compiler for SolcCompiler {
fn standard_json( fn standard_json(
&mut self, &mut self,
mut input: StandardJsonInput, mut input: StandardJsonInput,
pipeline: Pipeline,
base_path: Option<String>, base_path: Option<String>,
include_paths: Vec<String>, include_paths: Vec<String>,
allow_paths: Option<String>, allow_paths: Option<String>,
) -> anyhow::Result<StandardJsonOutput> { ) -> anyhow::Result<StandardJsonOutput> {
let version = self.version()?; let version = self.version()?.validate(&include_paths)?.default;
let mut command = std::process::Command::new(self.executable.as_str()); let mut command = std::process::Command::new(self.executable.as_str());
command.stdin(std::process::Stdio::piped()); command.stdin(std::process::Stdio::piped());
command.stdout(std::process::Stdio::piped()); command.stdout(std::process::Stdio::piped());
command.arg("--standard-json"); command.arg("--standard-json");
if let Some(base_path) = &base_path {
if !FIRST_SUPPORTS_BASE_PATH.matches(&version.default) {
anyhow::bail!(
"--base-path not supported this version {} of solc",
&version.default
);
}
command.arg("--base-path").arg(base_path);
}
if !include_paths.is_empty() && !FIRST_SUPPORTS_INCLUDE_PATH.matches(&version.default) {
anyhow::bail!(
"--include-path not supported this version {} of solc",
&version.default
);
}
for include_path in include_paths.into_iter() { for include_path in include_paths.into_iter() {
command.arg("--include-path"); command.arg("--include-path");
command.arg(include_path); command.arg(include_path);
} }
if let Some(base_path) = base_path {
command.arg("--base-path");
command.arg(base_path);
}
if let Some(allow_paths) = allow_paths { if let Some(allow_paths) = allow_paths {
command.arg("--allow-paths"); command.arg("--allow-paths");
command.arg(allow_paths); command.arg(allow_paths);
} }
input.normalize(&version.default); input.normalize(&version);
let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default(); let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default();
@@ -129,7 +108,7 @@ impl Compiler for SolcCompiler {
), ),
) )
})?; })?;
output.preprocess_ast(&version, pipeline, suppressed_warnings.as_slice())?; output.preprocess_ast(suppressed_warnings.as_slice())?;
Ok(output) Ok(output)
} }
@@ -163,8 +142,16 @@ impl Compiler for SolcCompiler {
anyhow::anyhow!("{} subprocess error: {:?}", self.executable, error) anyhow::anyhow!("{} subprocess error: {:?}", self.executable, error)
})?; })?;
if !output.status.success() { if !output.status.success() {
println!("{}", String::from_utf8_lossy(output.stdout.as_slice())); writeln!(
println!("{}", String::from_utf8_lossy(output.stderr.as_slice())); std::io::stdout(),
"{}",
String::from_utf8_lossy(output.stdout.as_slice())
)?;
writeln!(
std::io::stdout(),
"{}",
String::from_utf8_lossy(output.stderr.as_slice())
)?;
anyhow::bail!( anyhow::bail!(
"{} error: {}", "{} error: {}",
self.executable, self.executable,
@@ -228,10 +215,6 @@ impl Compiler for SolcCompiler {
/// The `solc --version` mini-parser. /// The `solc --version` mini-parser.
fn version(&mut self) -> anyhow::Result<Version> { fn version(&mut self) -> anyhow::Result<Version> {
if let Some(version) = self.version.as_ref() {
return Ok(version.to_owned());
}
let mut command = std::process::Command::new(self.executable.as_str()); let mut command = std::process::Command::new(self.executable.as_str());
command.arg("--version"); command.arg("--version");
let output = command.output().map_err(|error| { let output = command.output().map_err(|error| {
@@ -277,24 +260,6 @@ impl Compiler for SolcCompiler {
.and_then(|line| line.split('-').nth(1)) .and_then(|line| line.split('-').nth(1))
.and_then(|version| version.parse().ok()); .and_then(|version| version.parse().ok());
let version = Version::new(long, default, l2_revision); Ok(Version::new(long, default, l2_revision))
if version.default < super::FIRST_SUPPORTED_VERSION {
anyhow::bail!(
"`solc` versions <{} are not supported, found {}",
super::FIRST_SUPPORTED_VERSION,
version.default
);
}
if version.default > super::LAST_SUPPORTED_VERSION {
anyhow::bail!(
"`solc` versions >{} are not supported, found {}",
super::LAST_SUPPORTED_VERSION,
version.default
);
}
self.version = Some(version.clone());
Ok(version)
} }
} }
+20 -34
View File
@@ -1,10 +1,9 @@
//! The Solidity compiler. //! The Solidity compiler solJson interface.
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use crate::solc::combined_json::CombinedJson; use crate::solc::combined_json::CombinedJson;
use crate::solc::pipeline::Pipeline;
use crate::solc::standard_json::input::Input as StandardJsonInput; use crate::solc::standard_json::input::Input as StandardJsonInput;
use crate::solc::standard_json::output::Output as StandardJsonOutput; use crate::solc::standard_json::output::Output as StandardJsonOutput;
use crate::solc::version::Version; use crate::solc::version::Version;
@@ -19,23 +18,30 @@ extern "C" {
} }
/// The Solidity compiler. /// The Solidity compiler.
pub struct SoljsonCompiler { pub struct SoljsonCompiler;
/// The lazily-initialized compiler version.
pub version: Option<Version>,
}
impl Compiler for SoljsonCompiler { impl Compiler for SoljsonCompiler {
/// Compiles the Solidity `--standard-json` input into Yul IR. /// Compiles the Solidity `--standard-json` input into Yul IR.
fn standard_json( fn standard_json(
&mut self, &mut self,
mut input: StandardJsonInput, mut input: StandardJsonInput,
pipeline: Pipeline, base_path: Option<String>,
_base_path: Option<String>, include_paths: Vec<String>,
_include_paths: Vec<String>, allow_paths: Option<String>,
_allow_paths: Option<String>,
) -> anyhow::Result<StandardJsonOutput> { ) -> anyhow::Result<StandardJsonOutput> {
let version = self.version()?; if !include_paths.is_empty() {
input.normalize(&version.default); anyhow::bail!("configuring include paths is not supported with solJson")
}
if base_path.is_some() {
anyhow::bail!("configuring the base path is not supported with solJson")
}
if allow_paths.is_some() {
anyhow::bail!("configuring allow paths is not supported with solJson")
}
let version = self.version()?.validate(&include_paths)?.default;
input.normalize(&version);
let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default(); let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default();
let input_json = serde_json::to_string(&input).expect("Always valid"); let input_json = serde_json::to_string(&input).expect("Always valid");
@@ -50,7 +56,7 @@ impl Compiler for SoljsonCompiler {
.unwrap_or_else(|_| String::from_utf8_lossy(out.as_bytes()).to_string()), .unwrap_or_else(|_| String::from_utf8_lossy(out.as_bytes()).to_string()),
) )
})?; })?;
output.preprocess_ast(&version, pipeline, suppressed_warnings.as_slice())?; output.preprocess_ast(suppressed_warnings.as_slice())?;
Ok(output) Ok(output)
} }
@@ -76,31 +82,11 @@ impl Compiler for SoljsonCompiler {
.ok_or_else(|| anyhow::anyhow!("Soljson version parsing: metadata dropping"))? .ok_or_else(|| anyhow::anyhow!("Soljson version parsing: metadata dropping"))?
.parse() .parse()
.map_err(|error| anyhow::anyhow!("Soljson version parsing: {}", error))?; .map_err(|error| anyhow::anyhow!("Soljson version parsing: {}", error))?;
let l2_revision: Option<semver::Version> = version let l2_revision: Option<semver::Version> = version
.split('-') .split('-')
.nth(1) .nth(1)
.and_then(|version| version.parse().ok()); .and_then(|version| version.parse().ok());
Ok(Version::new(long, default, l2_revision))
let version = Version::new(long, default, l2_revision);
if version.default < super::FIRST_SUPPORTED_VERSION {
anyhow::bail!(
"`Soljson` versions <{} are not supported, found {}",
super::FIRST_SUPPORTED_VERSION,
version.default
);
}
if version.default > super::LAST_SUPPORTED_VERSION {
anyhow::bail!(
"`Soljson` versions >{} are not supported, found {}",
super::LAST_SUPPORTED_VERSION,
version.default
);
}
self.version = Some(version.clone());
Ok(version)
} }
} }
@@ -13,7 +13,6 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator};
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use crate::solc::pipeline::Pipeline as SolcPipeline;
use crate::solc::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata; use crate::solc::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata;
use crate::solc::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer; use crate::solc::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
use crate::solc::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection; use crate::solc::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection;
@@ -40,13 +39,13 @@ pub struct Input {
impl Input { impl Input {
/// A shortcut constructor from stdin. /// A shortcut constructor from stdin.
pub fn try_from_stdin(solc_pipeline: SolcPipeline) -> anyhow::Result<Self> { pub fn try_from_stdin() -> anyhow::Result<Self> {
let mut input: Self = serde_json::from_reader(std::io::BufReader::new(std::io::stdin()))?; let mut input: Self = serde_json::from_reader(std::io::BufReader::new(std::io::stdin()))?;
input input
.settings .settings
.output_selection .output_selection
.get_or_insert_with(SolcStandardJsonInputSettingsSelection::default) .get_or_insert_with(SolcStandardJsonInputSettingsSelection::default)
.extend_with_required(solc_pipeline); .extend_with_required();
Ok(input) Ok(input)
} }
@@ -61,15 +60,16 @@ impl Input {
output_selection: SolcStandardJsonInputSettingsSelection, output_selection: SolcStandardJsonInputSettingsSelection,
optimizer: SolcStandardJsonInputSettingsOptimizer, optimizer: SolcStandardJsonInputSettingsOptimizer,
metadata: Option<SolcStandardJsonInputSettingsMetadata>, metadata: Option<SolcStandardJsonInputSettingsMetadata>,
via_ir: bool,
suppressed_warnings: Option<Vec<Warning>>, suppressed_warnings: Option<Vec<Warning>>,
) -> anyhow::Result<Self> { ) -> anyhow::Result<Self> {
#[cfg(feature = "parallel")] let mut paths: BTreeSet<PathBuf> = paths.iter().cloned().collect();
let iter = paths.into_par_iter(); // Parallel iterator let libraries = Settings::parse_libraries(library_map)?;
for library_file in libraries.keys() {
paths.insert(PathBuf::from(library_file));
}
#[cfg(not(feature = "parallel"))] let sources = paths
let iter = paths.iter(); // Sequential iterator .iter()
let sources = iter
.map(|path| { .map(|path| {
let source = Source::try_from(path.as_path()).unwrap_or_else(|error| { let source = Source::try_from(path.as_path()).unwrap_or_else(|error| {
panic!("Source code file {path:?} reading error: {error}") panic!("Source code file {path:?} reading error: {error}")
@@ -78,8 +78,6 @@ impl Input {
}) })
.collect(); .collect();
let libraries = Settings::parse_libraries(library_map)?;
Ok(Self { Ok(Self {
language, language,
sources, sources,
@@ -88,7 +86,6 @@ impl Input {
libraries, libraries,
remappings, remappings,
output_selection, output_selection,
via_ir,
optimizer, optimizer,
metadata, metadata,
), ),
@@ -107,7 +104,6 @@ impl Input {
output_selection: SolcStandardJsonInputSettingsSelection, output_selection: SolcStandardJsonInputSettingsSelection,
optimizer: SolcStandardJsonInputSettingsOptimizer, optimizer: SolcStandardJsonInputSettingsOptimizer,
metadata: Option<SolcStandardJsonInputSettingsMetadata>, metadata: Option<SolcStandardJsonInputSettingsMetadata>,
via_ir: bool,
suppressed_warnings: Option<Vec<Warning>>, suppressed_warnings: Option<Vec<Warning>>,
) -> anyhow::Result<Self> { ) -> anyhow::Result<Self> {
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
@@ -127,7 +123,6 @@ impl Input {
libraries, libraries,
remappings, remappings,
output_selection, output_selection,
via_ir,
optimizer, optimizer,
metadata, metadata,
), ),
@@ -51,7 +51,6 @@ impl Settings {
libraries: BTreeMap<String, BTreeMap<String, String>>, libraries: BTreeMap<String, BTreeMap<String, String>>,
remappings: Option<BTreeSet<String>>, remappings: Option<BTreeSet<String>>,
output_selection: Selection, output_selection: Selection,
via_ir: bool,
optimizer: Optimizer, optimizer: Optimizer,
metadata: Option<Metadata>, metadata: Option<Metadata>,
) -> Self { ) -> Self {
@@ -60,9 +59,9 @@ impl Settings {
libraries: Some(libraries), libraries: Some(libraries),
remappings, remappings,
output_selection: Some(output_selection), output_selection: Some(output_selection),
via_ir: if via_ir { Some(true) } else { None },
optimizer, optimizer,
metadata, metadata,
via_ir: Some(true),
} }
} }
@@ -3,12 +3,8 @@
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use crate::solc::pipeline::Pipeline as SolcPipeline;
/// The `solc --standard-json` expected output selection flag. /// The `solc --standard-json` expected output selection flag.
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types)]
#[allow(clippy::upper_case_acronyms)]
pub enum Flag { pub enum Flag {
/// The ABI JSON. /// The ABI JSON.
#[serde(rename = "abi")] #[serde(rename = "abi")]
@@ -46,15 +42,6 @@ pub enum Flag {
Assembly, Assembly,
} }
impl From<SolcPipeline> for Flag {
fn from(pipeline: SolcPipeline) -> Self {
match pipeline {
SolcPipeline::Yul => Self::Yul,
SolcPipeline::EVMLA => Self::EVMLA,
}
}
}
impl std::fmt::Display for Flag { impl std::fmt::Display for Flag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
@@ -7,8 +7,6 @@ use std::collections::HashSet;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use crate::solc::pipeline::Pipeline as SolcPipeline;
use self::flag::Flag as SelectionFlag; use self::flag::Flag as SelectionFlag;
/// The `solc --standard-json` output file selection. /// The `solc --standard-json` output file selection.
@@ -24,7 +22,7 @@ pub struct File {
impl File { impl File {
/// Creates the selection required by our compilation process. /// Creates the selection required by our compilation process.
pub fn new_required(pipeline: SolcPipeline) -> Self { pub fn new_required() -> Self {
Self { Self {
per_file: Some(HashSet::from_iter([SelectionFlag::AST])), per_file: Some(HashSet::from_iter([SelectionFlag::AST])),
per_contract: Some(HashSet::from_iter([ per_contract: Some(HashSet::from_iter([
@@ -32,14 +30,14 @@ impl File {
SelectionFlag::EVMDBC, SelectionFlag::EVMDBC,
SelectionFlag::MethodIdentifiers, SelectionFlag::MethodIdentifiers,
SelectionFlag::Metadata, SelectionFlag::Metadata,
SelectionFlag::from(pipeline), SelectionFlag::Yul,
])), ])),
} }
} }
/// Extends the user's output selection with flag required by our compilation process. /// Extends the user's output selection with flag required by our compilation process.
pub fn extend_with_required(&mut self, pipeline: SolcPipeline) -> &mut Self { pub fn extend_with_required(&mut self) -> &mut Self {
let required = Self::new_required(pipeline); let required = Self::new_required();
self.per_file self.per_file
.get_or_insert_with(HashSet::default) .get_or_insert_with(HashSet::default)
@@ -5,8 +5,6 @@ pub mod file;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use crate::solc::pipeline::Pipeline as SolcPipeline;
use self::file::File as FileSelection; use self::file::File as FileSelection;
/// The `solc --standard-json` output selection. /// The `solc --standard-json` output selection.
@@ -19,17 +17,17 @@ pub struct Selection {
impl Selection { impl Selection {
/// Creates the selection required by our compilation process. /// Creates the selection required by our compilation process.
pub fn new_required(pipeline: SolcPipeline) -> Self { pub fn new_required() -> Self {
Self { Self {
all: Some(FileSelection::new_required(pipeline)), all: Some(FileSelection::new_required()),
} }
} }
/// Extends the user's output selection with flag required by our compilation process. /// Extends the user's output selection with flag required by our compilation process.
pub fn extend_with_required(&mut self, pipeline: SolcPipeline) -> &mut Self { pub fn extend_with_required(&mut self) -> &mut Self {
self.all self.all
.get_or_insert_with(|| FileSelection::new_required(pipeline)) .get_or_insert_with(FileSelection::new_required)
.extend_with_required(pipeline); .extend_with_required();
self self
} }
} }
@@ -1,46 +0,0 @@
//! The `solc --standard-json` output contract EVM extra metadata.
pub mod recursive_function;
use serde::Deserialize;
use serde::Serialize;
use self::recursive_function::RecursiveFunction;
/// The `solc --standard-json` output contract EVM extra metadata.
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ExtraMetadata {
/// The list of recursive functions.
#[serde(default = "Vec::new")]
pub recursive_functions: Vec<RecursiveFunction>,
}
impl ExtraMetadata {
/// Returns the recursive function reference for the specified tag.
pub fn get(
&self,
block_key: &revive_llvm_context::PolkaVMFunctionBlockKey,
) -> Option<&RecursiveFunction> {
for function in self.recursive_functions.iter() {
match block_key.code_type {
revive_llvm_context::PolkaVMCodeType::Deploy => {
if let Some(creation_tag) = function.creation_tag {
if num::BigUint::from(creation_tag) == block_key.tag {
return Some(function);
}
}
}
revive_llvm_context::PolkaVMCodeType::Runtime => {
if let Some(runtime_tag) = function.runtime_tag {
if num::BigUint::from(runtime_tag) == block_key.tag {
return Some(function);
}
}
}
}
}
None
}
}
@@ -1,22 +0,0 @@
//! The `solc --standard-json` output contract EVM recursive function.
use serde::Deserialize;
use serde::Serialize;
/// The `solc --standard-json` output contract EVM recursive function.
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct RecursiveFunction {
/// The function name.
pub name: String,
/// The creation code function block tag.
pub creation_tag: Option<usize>,
/// The runtime code function block tag.
pub runtime_tag: Option<usize>,
/// The number of input arguments.
#[serde(rename = "totalParamSize")]
pub input_size: usize,
/// The number of output arguments.
#[serde(rename = "totalRetParamSize")]
pub output_size: usize,
}
@@ -1,27 +1,20 @@
//! The `solc --standard-json` output contract EVM data. //! The `solc --standard-json` output contract EVM data.
pub mod bytecode; pub mod bytecode;
pub mod extra_metadata;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use crate::evmla::assembly::Assembly;
use self::bytecode::Bytecode; use self::bytecode::Bytecode;
use self::bytecode::DeployedBytecode; use self::bytecode::DeployedBytecode;
use self::extra_metadata::ExtraMetadata;
/// The `solc --standard-json` output contract EVM data. /// The `solc --standard-json` output contract EVM data.
/// It is replaced by PolkaVM data after compiling. /// It is replaced by PolkaVM data after compiling.
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct EVM { pub struct EVM {
/// The contract EVM legacy assembly code.
#[serde(rename = "legacyAssembly", skip_serializing_if = "Option::is_none")]
pub assembly: Option<Assembly>,
/// The contract PolkaVM assembly code. /// The contract PolkaVM assembly code.
#[serde(rename = "assembly", skip_serializing_if = "Option::is_none")] #[serde(rename = "assembly", skip_serializing_if = "Option::is_none")]
pub assembly_text: Option<String>, pub assembly_text: Option<String>,
@@ -37,9 +30,6 @@ pub struct EVM {
/// The contract function signatures. /// The contract function signatures.
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub method_identifiers: Option<BTreeMap<String, String>>, pub method_identifiers: Option<BTreeMap<String, String>>,
/// The extra EVMLA metadata.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extra_metadata: Option<ExtraMetadata>,
} }
impl EVM { impl EVM {
@@ -33,12 +33,11 @@ impl Error {
/// Returns the `ecrecover` function usage warning. /// Returns the `ecrecover` function usage warning.
pub fn message_ecrecover(src: Option<&str>) -> Self { pub fn message_ecrecover(src: Option<&str>) -> Self {
let message = r#" let message = r#"
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ Warning: It looks like you are using 'ecrecover' to validate a signature of a user account.
│ Warning: It looks like you are using 'ecrecover' to validate a signature of a user account. │ Polkadot comes with native account abstraction support, therefore it is highly recommended NOT
│ Polkadot comes with native account abstraction support, therefore it is highly recommended NOT │ to rely on the fact that the account has an ECDSA private key attached to it since accounts might
│ to rely on the fact that the account has an ECDSA private key attached to it since accounts might│ implement other signature schemes.
│ implement other signature schemes. │ "#
└──────────────────────────────────────────────────────────────────────────────────────────────────┘"#
.to_owned(); .to_owned();
Self { Self {
@@ -55,16 +54,15 @@ impl Error {
/// Returns the `<address payable>`'s `send` and `transfer` methods usage error. /// Returns the `<address payable>`'s `send` and `transfer` methods usage error.
pub fn message_send_and_transfer(src: Option<&str>) -> Self { pub fn message_send_and_transfer(src: Option<&str>) -> Self {
let message = r#" let message = r#"
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ Warning: It looks like you are using '<address payable>.send/transfer(<X>)'.
│ Warning: It looks like you are using '<address payable>.send/transfer(<X>)' without providing │ Using '<address payable>.send/transfer(<X>)' is deprecated and strongly discouraged!
│ the gas amount. Such calls will fail depending on the pubdata costs. │ The resolc compiler uses a heuristic to detect '<address payable>.send/transfer(<X>)' calls,
│ This might be a false positive if you are using an interface (like IERC20) instead of the │ which disables call re-entrancy and supplies all remaining gas instead of the 2300 gas stipend.
│ native Solidity `send/transfer`. │ However, detection is not guaranteed. You are advised to carefully test this, employ
│ Please use 'payable(<address>).call{value: <X>}("")' instead, but be careful with the reentrancy │ re-entrancy guards or use the withdrawal pattern instead!
│ attack. `send` and `transfer` send limited amount of gas that prevents reentrancy, whereas │ Learn more on https://docs.soliditylang.org/en/latest/security-considerations.html#reentrancy
│ `<address>.call{value: <X>}` sends all gas to the callee. Learn more on │ and https://docs.soliditylang.org/en/latest/common-patterns.html#withdrawal-from-contracts
│ https://docs.soliditylang.org/en/latest/security-considerations.html#reentrancy │ "#
└──────────────────────────────────────────────────────────────────────────────────────────────────┘"#
.to_owned(); .to_owned();
Self { Self {
@@ -81,15 +79,14 @@ impl Error {
/// Returns the `extcodesize` instruction usage warning. /// Returns the `extcodesize` instruction usage warning.
pub fn message_extcodesize(src: Option<&str>) -> Self { pub fn message_extcodesize(src: Option<&str>) -> Self {
let message = r#" let message = r#"
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ Warning: Your code or one of its dependencies uses the 'extcodesize' instruction, which is
│ Warning: Your code or one of its dependencies uses the 'extcodesize' instruction, which is │ usually needed in the following cases:
│ usually needed in the following cases: │ 1. To detect whether an address belongs to a smart contract.
1. To detect whether an address belongs to a smart contract. │ 2. To detect whether the deploy code execution has finished.
│ 2. To detect whether the deploy code execution has finished. │ Polkadot comes with native account abstraction support (so smart contracts are just accounts
│ Polkadot comes with native account abstraction support (so smart contracts are just accounts │ coverned by code), and you should avoid differentiating between contracts and non-contract
│ coverned by code), and you should avoid differentiating between contracts and non-contract | addresses.
| addresses. │ "#
└──────────────────────────────────────────────────────────────────────────────────────────────────┘"#
.to_owned(); .to_owned();
Self { Self {
@@ -106,12 +103,11 @@ impl Error {
/// Returns the `origin` instruction usage warning. /// Returns the `origin` instruction usage warning.
pub fn message_tx_origin(src: Option<&str>) -> Self { pub fn message_tx_origin(src: Option<&str>) -> Self {
let message = r#" let message = r#"
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ Warning: You are checking for 'tx.origin' in your code, which might lead to unexpected behavior.
│ Warning: You are checking for 'tx.origin' in your code, which might lead to unexpected behavior. │ Polkadot comes with native account abstraction support, and therefore the initiator of a
│ Polkadot comes with native account abstraction support, and therefore the initiator of a │ transaction might be different from the contract calling your code. It is highly recommended NOT
│ transaction might be different from the contract calling your code. It is highly recommended NOT │ to rely on tx.origin, but use msg.sender instead.
│ to rely on tx.origin, but use msg.sender instead. │ "#
└──────────────────────────────────────────────────────────────────────────────────────────────────┘"#
.to_owned(); .to_owned();
Self { Self {
@@ -125,26 +121,6 @@ impl Error {
} }
} }
/// Returns the internal function pointer usage error.
pub fn message_internal_function_pointer(src: Option<&str>) -> Self {
let message = r#"
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Error: Internal function pointers are not supported in EVM legacy assembly pipeline. │
│ Please use the Yul IR codegen instead. │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘"#
.to_owned();
Self {
component: "general".to_owned(),
error_code: None,
formatted_message: message.clone(),
message,
severity: "error".to_owned(),
source_location: src.map(SourceLocation::from_str).and_then(Result::ok),
r#type: "Error".to_owned(),
}
}
/// Appends the contract path to the message.. /// Appends the contract path to the message..
pub fn push_contract_path(&mut self, path: &str) { pub fn push_contract_path(&mut self, path: &str) {
self.formatted_message self.formatted_message
@@ -10,12 +10,9 @@ use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use sha3::Digest; use sha3::Digest;
use crate::evmla::assembly::instruction::Instruction;
use crate::evmla::assembly::Assembly;
use crate::project::contract::ir::IR as ProjectContractIR; use crate::project::contract::ir::IR as ProjectContractIR;
use crate::project::contract::Contract as ProjectContract; use crate::project::contract::Contract as ProjectContract;
use crate::project::Project; use crate::project::Project;
use crate::solc::pipeline::Pipeline as SolcPipeline;
use crate::solc::version::Version as SolcVersion; use crate::solc::version::Version as SolcVersion;
use crate::warning::Warning; use crate::warning::Warning;
use crate::yul::lexer::Lexer; use crate::yul::lexer::Lexer;
@@ -53,14 +50,9 @@ impl Output {
&mut self, &mut self,
source_code_files: BTreeMap<String, String>, source_code_files: BTreeMap<String, String>,
libraries: BTreeMap<String, BTreeMap<String, String>>, libraries: BTreeMap<String, BTreeMap<String, String>>,
pipeline: SolcPipeline,
solc_version: &SolcVersion, solc_version: &SolcVersion,
debug_config: &revive_llvm_context::DebugConfig, debug_config: &revive_llvm_context::DebugConfig,
) -> anyhow::Result<Project> { ) -> anyhow::Result<Project> {
if let SolcPipeline::EVMLA = pipeline {
self.preprocess_dependencies()?;
}
let files = match self.contracts.as_ref() { let files = match self.contracts.as_ref() {
Some(files) => files, Some(files) => files,
None => match &self.errors { None => match &self.errors {
@@ -76,8 +68,6 @@ impl Output {
for (name, contract) in contracts.iter() { for (name, contract) in contracts.iter() {
let full_path = format!("{path}:{name}"); let full_path = format!("{path}:{name}");
let source = match pipeline {
SolcPipeline::Yul => {
let ir_optimized = match contract.ir_optimized.to_owned() { let ir_optimized = match contract.ir_optimized.to_owned() {
Some(ir_optimized) => ir_optimized, Some(ir_optimized) => ir_optimized,
None => continue, None => continue,
@@ -93,21 +83,7 @@ impl Output {
anyhow::anyhow!("Contract `{}` parsing error: {:?}", full_path, error) anyhow::anyhow!("Contract `{}` parsing error: {:?}", full_path, error)
})?; })?;
ProjectContractIR::new_yul(ir_optimized.to_owned(), object) let source = ProjectContractIR::new_yul(ir_optimized.to_owned(), object);
}
SolcPipeline::EVMLA => {
let evm = contract.evm.as_ref();
let assembly = match evm.and_then(|evm| evm.assembly.to_owned()) {
Some(assembly) => assembly.to_owned(),
None => continue,
};
let extra_metadata = evm
.and_then(|evm| evm.extra_metadata.to_owned())
.unwrap_or_default();
ProjectContractIR::new_evmla(assembly, extra_metadata)
}
};
let source_code = source_code_files let source_code = source_code_files
.get(path.as_str()) .get(path.as_str())
@@ -133,12 +109,7 @@ impl Output {
} }
/// Traverses the AST and returns the list of additional errors and warnings. /// Traverses the AST and returns the list of additional errors and warnings.
pub fn preprocess_ast( pub fn preprocess_ast(&mut self, suppressed_warnings: &[Warning]) -> anyhow::Result<()> {
&mut self,
version: &SolcVersion,
pipeline: SolcPipeline,
suppressed_warnings: &[Warning],
) -> anyhow::Result<()> {
let sources = match self.sources.as_ref() { let sources = match self.sources.as_ref() {
Some(sources) => sources, Some(sources) => sources,
None => return Ok(()), None => return Ok(()),
@@ -147,8 +118,7 @@ impl Output {
let mut messages = Vec::new(); let mut messages = Vec::new();
for (path, source) in sources.iter() { for (path, source) in sources.iter() {
if let Some(ast) = source.ast.as_ref() { if let Some(ast) = source.ast.as_ref() {
let mut polkavm_messages = let mut polkavm_messages = Source::get_messages(ast, suppressed_warnings);
Source::get_messages(ast, version, pipeline, suppressed_warnings);
for message in polkavm_messages.iter_mut() { for message in polkavm_messages.iter_mut() {
message.push_contract_path(path.as_str()); message.push_contract_path(path.as_str());
} }
@@ -165,83 +135,4 @@ impl Output {
Ok(()) Ok(())
} }
/// The pass, which replaces with dependency indexes with actual data.
fn preprocess_dependencies(&mut self) -> anyhow::Result<()> {
let files = match self.contracts.as_mut() {
Some(files) => files,
None => return Ok(()),
};
let mut hash_path_mapping = BTreeMap::new();
for (path, contracts) in files.iter() {
for (name, contract) in contracts.iter() {
let full_path = format!("{path}:{name}");
let hash = match contract
.evm
.as_ref()
.and_then(|evm| evm.assembly.as_ref())
.map(|assembly| assembly.keccak256())
{
Some(hash) => hash,
None => continue,
};
hash_path_mapping.insert(hash, full_path);
}
}
for (path, contracts) in files.iter_mut() {
for (name, contract) in contracts.iter_mut() {
let assembly = match contract.evm.as_mut().and_then(|evm| evm.assembly.as_mut()) {
Some(assembly) => assembly,
None => continue,
};
let full_path = format!("{path}:{name}");
Self::preprocess_dependency_level(
full_path.as_str(),
assembly,
&hash_path_mapping,
)?;
}
}
Ok(())
}
/// Preprocesses an assembly JSON structure dependency data map.
fn preprocess_dependency_level(
full_path: &str,
assembly: &mut Assembly,
hash_path_mapping: &BTreeMap<String, String>,
) -> anyhow::Result<()> {
assembly.set_full_path(full_path.to_owned());
let deploy_code_index_path_mapping =
assembly.deploy_dependencies_pass(full_path, hash_path_mapping)?;
if let Some(deploy_code_instructions) = assembly.code.as_deref_mut() {
Instruction::replace_data_aliases(
deploy_code_instructions,
&deploy_code_index_path_mapping,
)?;
};
let runtime_code_index_path_mapping =
assembly.runtime_dependencies_pass(full_path, hash_path_mapping)?;
if let Some(runtime_code_instructions) = assembly
.data
.as_mut()
.and_then(|data_map| data_map.get_mut("0"))
.and_then(|data| data.get_assembly_mut())
.and_then(|assembly| assembly.code.as_deref_mut())
{
Instruction::replace_data_aliases(
runtime_code_instructions,
&runtime_code_index_path_mapping,
)?;
}
Ok(())
}
} }
@@ -3,9 +3,7 @@
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use crate::solc::pipeline::Pipeline as SolcPipeline;
use crate::solc::standard_json::output::error::Error as SolcStandardJsonOutputError; use crate::solc::standard_json::output::error::Error as SolcStandardJsonOutputError;
use crate::solc::version::Version as SolcVersion;
use crate::warning::Warning; use crate::warning::Warning;
/// The `solc --standard-json` output source. /// The `solc --standard-json` output source.
@@ -132,37 +130,9 @@ impl Source {
)) ))
} }
/// Checks the AST node for the internal function pointers value usage.
pub fn check_internal_function_pointer(
ast: &serde_json::Value,
) -> Option<SolcStandardJsonOutputError> {
let ast = ast.as_object()?;
if ast.get("nodeType")?.as_str()? != "VariableDeclaration" {
return None;
}
let type_descriptions = ast.get("typeDescriptions")?.as_object()?;
if !type_descriptions
.get("typeIdentifier")?
.as_str()?
.contains("function_internal")
{
return None;
}
Some(
SolcStandardJsonOutputError::message_internal_function_pointer(
ast.get("src")?.as_str(),
),
)
}
/// Returns the list of messages for some specific parts of the AST. /// Returns the list of messages for some specific parts of the AST.
pub fn get_messages( pub fn get_messages(
ast: &serde_json::Value, ast: &serde_json::Value,
version: &SolcVersion,
pipeline: SolcPipeline,
suppressed_warnings: &[Warning], suppressed_warnings: &[Warning],
) -> Vec<SolcStandardJsonOutputError> { ) -> Vec<SolcStandardJsonOutputError> {
let mut messages = Vec::new(); let mut messages = Vec::new();
@@ -189,31 +159,16 @@ impl Source {
messages.push(message); messages.push(message);
} }
} }
if SolcPipeline::EVMLA == pipeline && version.l2_revision.is_none() {
if let Some(message) = Self::check_internal_function_pointer(ast) {
messages.push(message);
}
}
match ast { match ast {
serde_json::Value::Array(array) => { serde_json::Value::Array(array) => {
for element in array.iter() { for element in array.iter() {
messages.extend(Self::get_messages( messages.extend(Self::get_messages(element, suppressed_warnings));
element,
version,
pipeline,
suppressed_warnings,
));
} }
} }
serde_json::Value::Object(object) => { serde_json::Value::Object(object) => {
for (_key, value) in object.iter() { for (_key, value) in object.iter() {
messages.extend(Self::get_messages( messages.extend(Self::get_messages(value, suppressed_warnings));
value,
version,
pipeline,
suppressed_warnings,
));
} }
} }
_ => {} _ => {}
+22
View File
@@ -36,4 +36,26 @@ impl Version {
l2_revision: None, l2_revision: None,
} }
} }
pub fn validate(self, include_paths: &[String]) -> anyhow::Result<Self> {
if self.default < super::FIRST_SUPPORTED_VERSION {
anyhow::bail!(
"`solc` versions <{} are not supported, found {}",
super::FIRST_SUPPORTED_VERSION,
self.default
);
}
if self.default > super::LAST_SUPPORTED_VERSION {
anyhow::bail!(
"`solc` versions >{} are not supported, found {}",
super::LAST_SUPPORTED_VERSION,
self.default
);
}
if !include_paths.is_empty() && self.default < super::FIRST_INCLUDE_PATH_VERSION {
anyhow::bail!("--include-path is not supported in solc {}", self.default);
}
Ok(self)
}
} }
+15 -38
View File
@@ -8,7 +8,6 @@ use std::sync::Mutex;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use crate::project::Project; use crate::project::Project;
use crate::solc::pipeline::Pipeline as SolcPipeline;
use crate::solc::solc_compiler::SolcCompiler; use crate::solc::solc_compiler::SolcCompiler;
use crate::solc::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer; use crate::solc::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
use crate::solc::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection; use crate::solc::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection;
@@ -30,8 +29,8 @@ const DEBUG_CONFIG: revive_llvm_context::DebugConfig =
#[derive(Hash, PartialEq, Eq)] #[derive(Hash, PartialEq, Eq)]
struct CachedBlob { struct CachedBlob {
contract_name: String, contract_name: String,
solidity: String,
solc_optimizer_enabled: bool, solc_optimizer_enabled: bool,
pipeline: SolcPipeline,
} }
/// Checks if the required executables are present in `${PATH}`. /// Checks if the required executables are present in `${PATH}`.
@@ -54,17 +53,9 @@ pub fn build_solidity(
sources: BTreeMap<String, String>, sources: BTreeMap<String, String>,
libraries: BTreeMap<String, BTreeMap<String, String>>, libraries: BTreeMap<String, BTreeMap<String, String>>,
remappings: Option<BTreeSet<String>>, remappings: Option<BTreeSet<String>>,
pipeline: SolcPipeline,
optimizer_settings: revive_llvm_context::OptimizerSettings, optimizer_settings: revive_llvm_context::OptimizerSettings,
) -> anyhow::Result<SolcStandardJsonOutput> { ) -> anyhow::Result<SolcStandardJsonOutput> {
build_solidity_with_options( build_solidity_with_options(sources, libraries, remappings, optimizer_settings, true)
sources,
libraries,
remappings,
pipeline,
optimizer_settings,
true,
)
} }
/// Builds the Solidity project and returns the standard JSON output. /// Builds the Solidity project and returns the standard JSON output.
@@ -74,7 +65,6 @@ pub fn build_solidity_with_options(
sources: BTreeMap<String, String>, sources: BTreeMap<String, String>,
libraries: BTreeMap<String, BTreeMap<String, String>>, libraries: BTreeMap<String, BTreeMap<String, String>>,
remappings: Option<BTreeSet<String>>, remappings: Option<BTreeSet<String>>,
pipeline: SolcPipeline,
optimizer_settings: revive_llvm_context::OptimizerSettings, optimizer_settings: revive_llvm_context::OptimizerSettings,
solc_optimizer_enabled: bool, solc_optimizer_enabled: bool,
) -> anyhow::Result<SolcStandardJsonOutput> { ) -> anyhow::Result<SolcStandardJsonOutput> {
@@ -93,7 +83,7 @@ pub fn build_solidity_with_options(
sources.clone(), sources.clone(),
libraries.clone(), libraries.clone(),
remappings, remappings,
SolcStandardJsonInputSettingsSelection::new_required(pipeline), SolcStandardJsonInputSettingsSelection::new_required(),
SolcStandardJsonInputSettingsOptimizer::new( SolcStandardJsonInputSettingsOptimizer::new(
solc_optimizer_enabled, solc_optimizer_enabled,
None, None,
@@ -101,14 +91,12 @@ pub fn build_solidity_with_options(
false, false,
), ),
None, None,
pipeline == SolcPipeline::Yul,
None, None,
)?; )?;
let mut output = solc.standard_json(input, pipeline, None, vec![], None)?; let mut output = solc.standard_json(input, None, vec![], None)?;
let project = let project = output.try_to_project(sources, libraries, &solc_version, &DEBUG_CONFIG)?;
output.try_to_project(sources, libraries, pipeline, &solc_version, &DEBUG_CONFIG)?;
let build: crate::Build = project.compile(optimizer_settings, false, DEBUG_CONFIG)?; let build: crate::Build = project.compile(optimizer_settings, false, DEBUG_CONFIG)?;
build.write_to_standard_json(&mut output, &solc_version)?; build.write_to_standard_json(&mut output, &solc_version)?;
@@ -121,7 +109,6 @@ pub fn build_solidity_with_options_evm(
sources: BTreeMap<String, String>, sources: BTreeMap<String, String>,
libraries: BTreeMap<String, BTreeMap<String, String>>, libraries: BTreeMap<String, BTreeMap<String, String>>,
remappings: Option<BTreeSet<String>>, remappings: Option<BTreeSet<String>>,
pipeline: SolcPipeline,
solc_optimizer_enabled: bool, solc_optimizer_enabled: bool,
) -> anyhow::Result<BTreeMap<String, (Bytecode, DeployedBytecode)>> { ) -> anyhow::Result<BTreeMap<String, (Bytecode, DeployedBytecode)>> {
check_dependencies(); check_dependencies();
@@ -139,7 +126,7 @@ pub fn build_solidity_with_options_evm(
sources.clone(), sources.clone(),
libraries.clone(), libraries.clone(),
remappings, remappings,
SolcStandardJsonInputSettingsSelection::new_required(pipeline), SolcStandardJsonInputSettingsSelection::new_required(),
SolcStandardJsonInputSettingsOptimizer::new( SolcStandardJsonInputSettingsOptimizer::new(
solc_optimizer_enabled, solc_optimizer_enabled,
None, None,
@@ -147,11 +134,10 @@ pub fn build_solidity_with_options_evm(
false, false,
), ),
None, None,
pipeline == SolcPipeline::Yul,
None, None,
)?; )?;
let mut output = solc.standard_json(input, pipeline, None, vec![], None)?; let mut output = solc.standard_json(input, None, vec![], None)?;
let mut contracts = BTreeMap::new(); let mut contracts = BTreeMap::new();
if let Some(files) = output.contracts.as_mut() { if let Some(files) = output.contracts.as_mut() {
@@ -176,7 +162,6 @@ pub fn build_solidity_with_options_evm(
pub fn build_solidity_and_detect_missing_libraries( pub fn build_solidity_and_detect_missing_libraries(
sources: BTreeMap<String, String>, sources: BTreeMap<String, String>,
libraries: BTreeMap<String, BTreeMap<String, String>>, libraries: BTreeMap<String, BTreeMap<String, String>>,
pipeline: SolcPipeline,
) -> anyhow::Result<SolcStandardJsonOutput> { ) -> anyhow::Result<SolcStandardJsonOutput> {
check_dependencies(); check_dependencies();
@@ -193,17 +178,15 @@ pub fn build_solidity_and_detect_missing_libraries(
sources.clone(), sources.clone(),
libraries.clone(), libraries.clone(),
None, None,
SolcStandardJsonInputSettingsSelection::new_required(pipeline), SolcStandardJsonInputSettingsSelection::new_required(),
SolcStandardJsonInputSettingsOptimizer::new(true, None, &solc_version.default, false), SolcStandardJsonInputSettingsOptimizer::new(true, None, &solc_version.default, false),
None, None,
pipeline == SolcPipeline::Yul,
None, None,
)?; )?;
let mut output = solc.standard_json(input, pipeline, None, vec![], None)?; let mut output = solc.standard_json(input, None, vec![], None)?;
let project = let project = output.try_to_project(sources, libraries, &solc_version, &DEBUG_CONFIG)?;
output.try_to_project(sources, libraries, pipeline, &solc_version, &DEBUG_CONFIG)?;
let missing_libraries = project.get_missing_libraries(); let missing_libraries = project.get_missing_libraries();
missing_libraries.write_to_standard_json(&mut output, &solc.version()?)?; missing_libraries.write_to_standard_json(&mut output, &solc.version()?)?;
@@ -234,7 +217,6 @@ pub fn check_solidity_warning(
source_code: &str, source_code: &str,
warning_substring: &str, warning_substring: &str,
libraries: BTreeMap<String, BTreeMap<String, String>>, libraries: BTreeMap<String, BTreeMap<String, String>>,
pipeline: SolcPipeline,
skip_for_revive_edition: bool, skip_for_revive_edition: bool,
suppressed_warnings: Option<Vec<Warning>>, suppressed_warnings: Option<Vec<Warning>>,
) -> anyhow::Result<bool> { ) -> anyhow::Result<bool> {
@@ -253,14 +235,13 @@ pub fn check_solidity_warning(
sources.clone(), sources.clone(),
libraries, libraries,
None, None,
SolcStandardJsonInputSettingsSelection::new_required(pipeline), SolcStandardJsonInputSettingsSelection::new_required(),
SolcStandardJsonInputSettingsOptimizer::new(true, None, &solc_version.default, false), SolcStandardJsonInputSettingsOptimizer::new(true, None, &solc_version.default, false),
None, None,
pipeline == SolcPipeline::Yul,
suppressed_warnings, suppressed_warnings,
)?; )?;
let output = solc.standard_json(input, pipeline, None, vec![], None)?; let output = solc.standard_json(input, None, vec![], None)?;
let contains_warning = output let contains_warning = output
.errors .errors
.ok_or_else(|| anyhow::anyhow!("Solidity compiler messages not found"))? .ok_or_else(|| anyhow::anyhow!("Solidity compiler messages not found"))?
@@ -273,7 +254,7 @@ pub fn check_solidity_warning(
/// Compile the blob of `contract_name` found in given `source_code`. /// Compile the blob of `contract_name` found in given `source_code`.
/// The `solc` optimizer will be enabled /// The `solc` optimizer will be enabled
pub fn compile_blob(contract_name: &str, source_code: &str) -> Vec<u8> { pub fn compile_blob(contract_name: &str, source_code: &str) -> Vec<u8> {
compile_blob_with_options(contract_name, source_code, true, SolcPipeline::Yul) compile_blob_with_options(contract_name, source_code, true)
} }
/// Compile the EVM bin-runtime of `contract_name` found in given `source_code`. /// Compile the EVM bin-runtime of `contract_name` found in given `source_code`.
@@ -298,10 +279,9 @@ fn compile_evm(
solc_optimizer_enabled: bool, solc_optimizer_enabled: bool,
runtime: bool, runtime: bool,
) -> Vec<u8> { ) -> Vec<u8> {
let pipeline = SolcPipeline::Yul;
let id = CachedBlob { let id = CachedBlob {
contract_name: contract_name.to_owned(), contract_name: contract_name.to_owned(),
pipeline, solidity: source_code.to_owned(),
solc_optimizer_enabled, solc_optimizer_enabled,
}; };
@@ -319,7 +299,6 @@ fn compile_evm(
[(file_name.into(), source_code.into())].into(), [(file_name.into(), source_code.into())].into(),
Default::default(), Default::default(),
None, None,
pipeline,
solc_optimizer_enabled, solc_optimizer_enabled,
) )
.expect("source should compile"); .expect("source should compile");
@@ -343,12 +322,11 @@ pub fn compile_blob_with_options(
contract_name: &str, contract_name: &str,
source_code: &str, source_code: &str,
solc_optimizer_enabled: bool, solc_optimizer_enabled: bool,
pipeline: SolcPipeline,
) -> Vec<u8> { ) -> Vec<u8> {
let id = CachedBlob { let id = CachedBlob {
contract_name: contract_name.to_owned(), contract_name: contract_name.to_owned(),
solidity: source_code.to_owned(),
solc_optimizer_enabled, solc_optimizer_enabled,
pipeline,
}; };
if let Some(blob) = PVM_BLOB_CACHE.lock().unwrap().get(&id) { if let Some(blob) = PVM_BLOB_CACHE.lock().unwrap().get(&id) {
@@ -360,7 +338,6 @@ pub fn compile_blob_with_options(
[(file_name.into(), source_code.into())].into(), [(file_name.into(), source_code.into())].into(),
Default::default(), Default::default(),
None, None,
pipeline,
revive_llvm_context::OptimizerSettings::cycles(), revive_llvm_context::OptimizerSettings::cycles(),
solc_optimizer_enabled, solc_optimizer_enabled,
) )
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="jest tests" tests="27" failures="0" errors="0" time="2.146">
<testsuite name="Run with --yul by default" errors="0" failures="0" skipped="1" timestamp="2024-10-24T17:08:50" time="1.508" tests="6">
<testcase classname="Run with --yul by default Valid command exit code = 0" name="Run with --yul by default Valid command exit code = 0" time="0.003">
</testcase>
<testcase classname="Run with --yul by default --yul output is presented" name="Run with --yul by default --yul output is presented" time="0">
</testcase>
<testcase classname="Run with --yul by default solc exit code == resolc exit code" name="Run with --yul by default solc exit code == resolc exit code" time="0">
<skipped/>
</testcase>
<testcase classname="Run with --yul by default run invalid: resolc --yul" name="Run with --yul by default run invalid: resolc --yul" time="0.001">
</testcase>
<testcase classname="Run with --yul by default Invalid command exit code = 1" name="Run with --yul by default Invalid command exit code = 1" time="0">
</testcase>
<testcase classname="Run with --yul by default Invalid solc exit code == Invalid resolc exit code" name="Run with --yul by default Invalid solc exit code == Invalid resolc exit code" time="0.041">
</testcase>
</testsuite>
<testsuite name="Run with --asm by default" errors="0" failures="0" skipped="0" timestamp="2024-10-24T17:08:50" time="1.512" tests="6">
<testcase classname="Run with --asm by default Valid command exit code = 0" name="Run with --asm by default Valid command exit code = 0" time="0.002">
</testcase>
<testcase classname="Run with --asm by default --asm output is presented" name="Run with --asm by default --asm output is presented" time="0.001">
</testcase>
<testcase classname="Run with --asm by default solc exit code == resolc exit code" name="Run with --asm by default solc exit code == resolc exit code" time="0.044">
</testcase>
<testcase classname="Run with --asm by default run invalid: resolc --asm" name="Run with --asm by default run invalid: resolc --asm" time="0">
</testcase>
<testcase classname="Run with --asm by default Invalid command exit code = 1" name="Run with --asm by default Invalid command exit code = 1" time="0.001">
</testcase>
<testcase classname="Run with --asm by default Invalid solc exit code == Invalid resolc exit code" name="Run with --asm by default Invalid solc exit code == Invalid resolc exit code" time="0.04">
</testcase>
</testsuite>
<testsuite name="Run resolc without any options" errors="0" failures="0" skipped="2" timestamp="2024-10-24T17:08:50" time="2.016" tests="15">
<testcase classname="Run resolc without any options Info with help is presented" name="Run resolc without any options Info with help is presented" time="0.002">
</testcase>
<testcase classname="Run resolc without any options Exit code = 1" name="Run resolc without any options Exit code = 1" time="0">
</testcase>
<testcase classname="Run resolc without any options solc exit code == resolc exit code" name="Run resolc without any options solc exit code == resolc exit code" time="0.044">
</testcase>
<testcase classname="Default run a command from the help Compiler run successful" name="Default run a command from the help Compiler run successful" time="0">
</testcase>
<testcase classname="Default run a command from the help Exit code = 0" name="Default run a command from the help Exit code = 0" time="0.001">
</testcase>
<testcase classname="Default run a command from the help Output dir is created" name="Default run a command from the help Output dir is created" time="0">
</testcase>
<testcase classname="Default run a command from the help Output file is created" name="Default run a command from the help Output file is created" time="0">
<skipped/>
</testcase>
<testcase classname="Default run a command from the help the output file is not empty" name="Default run a command from the help the output file is not empty" time="0">
</testcase>
<testcase classname="Default run a command from the help No &apos;Error&apos;/&apos;Warning&apos;/&apos;Fail&apos; in the output" name="Default run a command from the help No &apos;Error&apos;/&apos;Warning&apos;/&apos;Fail&apos; in the output" time="0">
</testcase>
<testcase classname="Default run a command from the help Compiler run successful" name="Default run a command from the help Compiler run successful" time="0.001">
</testcase>
<testcase classname="Default run a command from the help Exit code = 0" name="Default run a command from the help Exit code = 0" time="0">
</testcase>
<testcase classname="Default run a command from the help Output dir is created" name="Default run a command from the help Output dir is created" time="0">
</testcase>
<testcase classname="Default run a command from the help Output files are created" name="Default run a command from the help Output files are created" time="0">
<skipped/>
</testcase>
<testcase classname="Default run a command from the help the output files are not empty" name="Default run a command from the help the output files are not empty" time="0.003">
</testcase>
<testcase classname="Default run a command from the help No &apos;Error&apos;/&apos;Warning&apos;/&apos;Fail&apos; in the output" name="Default run a command from the help No &apos;Error&apos;/&apos;Warning&apos;/&apos;Fail&apos; in the output" time="0">
</testcase>
</testsuite>
</testsuites>
@@ -16,11 +16,11 @@
], ],
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.11", "@types/jest": "^29.5.14",
"@types/shelljs": "^0.8.15", "@types/shelljs": "^0.8.15",
"jest": "^29.7.0", "jest": "^29.7.0",
"shelljs": "^0.8.5", "shelljs": "^0.8.5",
"ts-jest": "^29.1.1", "ts-jest": "^29.2.5",
"typescript": "^5.3.3" "typescript": "^5.7.3"
} }
} }
@@ -10,7 +10,7 @@ describe("Run resolc without any options", () => {
const result = executeCommand(command); const result = executeCommand(command);
it("Info with help is presented", () => { it("Info with help is presented", () => {
expect(result.output).toMatch(/(No input sources specified|Error(s) found.)/i); expect(result.output).toMatch(/(Usage: resolc)/i);
}); });
it("Exit code = 1", () => { it("Exit code = 1", () => {
@@ -28,7 +28,7 @@ describe("Run resolc without any options", () => {
//#1713 //#1713
describe("Default run a command from the help", () => { describe("Default run a command from the help", () => {
const command = `resolc ${paths.pathToBasicSolContract} -O3 --bin --output-dir "${paths.pathToOutputDir}"`; // potential issue on resolc with full path on Windows cmd const command = `resolc ${paths.pathToBasicSolContract} --overwrite -O3 --bin --output-dir "${paths.pathToOutputDir}"`; // potential issue on resolc with full path on Windows cmd
const result = executeCommand(command); const result = executeCommand(command);
it("Compiler run successful", () => { it("Compiler run successful", () => {
@@ -54,7 +54,7 @@ describe("Default run a command from the help", () => {
//#1818 //#1818
describe("Default run a command from the help", () => { describe("Default run a command from the help", () => {
const command = `resolc ${paths.pathToBasicSolContract} -O3 --bin --asm --output-dir "${paths.pathToOutputDir}"`; // potential issue on resolc with full path on Windows cmd const command = `resolc ${paths.pathToBasicSolContract} --overwrite -O3 --bin --asm --output-dir "${paths.pathToOutputDir}"`; // potential issue on resolc with full path on Windows cmd
const result = executeCommand(command); const result = executeCommand(command);
it("Compiler run successful", () => { it("Compiler run successful", () => {
@@ -81,8 +81,8 @@ describe("Default run a command from the help", () => {
describe("Run resolc with source debug information", () => { describe("Run resolc with source debug information", () => {
const commands = [ const commands = [
`resolc -g ${paths.pathToBasicSolContract} --bin --asm --output-dir "${paths.pathToOutputDir}"`, `resolc -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`,
`resolc --disable-solc-optimizer -g ${paths.pathToBasicSolContract} --bin --asm --output-dir "${paths.pathToOutputDir}"` `resolc --disable-solc-optimizer -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`
]; // potential issue on resolc with full path on Windows cmd`; ]; // potential issue on resolc with full path on Windows cmd`;
for (var idx in commands) { for (var idx in commands) {
@@ -114,8 +114,8 @@ describe("Run resolc with source debug information", () => {
describe("Run resolc with source debug information, check LLVM debug-info", () => { describe("Run resolc with source debug information, check LLVM debug-info", () => {
const commands = [ const commands = [
`resolc -g ${paths.pathToBasicSolContract} --debug-output-dir="${paths.pathToOutputDir}"`, `resolc -g ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`,
`resolc -g --disable-solc-optimizer ${paths.pathToBasicSolContract} --debug-output-dir="${paths.pathToOutputDir}"` `resolc -g --disable-solc-optimizer ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`
]; // potential issue on resolc with full path on Windows cmd`; ]; // potential issue on resolc with full path on Windows cmd`;
for (var idx in commands) { for (var idx in commands) {
@@ -0,0 +1,153 @@
import { executeCommand } from "../src/helper";
import { paths } from '../src/entities';
describe("Set of --combined-json tests", () => {
const zksolcCommand = 'zksolc';
const solcCommand = 'solc';
const json_args: string[] = [`abi`, `hashes`, `metadata`, `devdoc`, `userdoc`, `storage-layout`, `ast`, `asm`, `bin`, `bin-runtime`];
//id1742:I
describe(`Run ${zksolcCommand} with just --combined-json`, () => {
const args = [`--combined-json`];
const result = executeCommand(zksolcCommand, args);
it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1);
});
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(requires a value but none was supplied)/i);
});
it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
//id1742:II
describe(`Run ${zksolcCommand} with Sol contract and --combined-json`, () => {
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`];
const result = executeCommand(zksolcCommand, args);
it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1);
});
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(requires a value but none was supplied)/i);
});
it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
//id1742:III
for (let i = 0; i < json_args.length; i++) {
describe(`Run ${zksolcCommand} with Sol, --combined-json and ARG: ${json_args[i]}`, () => {
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`, `${json_args[i]}`];
const result = executeCommand(zksolcCommand, args);
it("Valid command exit code = 0", () => {
expect(result.exitCode).toBe(0);
});
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(contracts)/i);
});
it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
}
//id1829:I
for (let i = 0; i < json_args.length; i++) {
describe(`Run ${zksolcCommand} with Sol, --combined-json and wrong ARG: --${json_args[i]}`, () => {
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`, `--${json_args[i]}`];
const result = executeCommand(zksolcCommand, args);
it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1);
});
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(Invalid option|error)/i);
});
it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
}
//id1829:II
for (let i = 0; i < json_args.length; i++) {
describe(`Run ${zksolcCommand} with Sol, --combined-json and multiple ARG: ${json_args[i]} ${json_args[i]}`, () => {
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`, `${json_args[i]}`, `${json_args[i]}`];
const result = executeCommand(zksolcCommand, args);
xit("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1);
});
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(No such file or directory|cannot find the file specified)/i); // Hopefully we should have more precise message here!
});
xit("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
}
//id1829:III
for (let i = 0; i < json_args.length; i++) {
describe(`Run ${zksolcCommand} with Sol, and multiple (--combined-json ${json_args[i]})`, () => {
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`, `${json_args[i]}`, `--combined-json`, `${json_args[i]}`];
const result = executeCommand(zksolcCommand, args);
it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1);
});
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(cannot be used multiple times)/i);
});
it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
}
//id1830
for (let i = 0; i < json_args.length; i++) {
describe(`Run ${zksolcCommand} with Yul, and --combined-json ${json_args[i]}`, () => {
const args = [`${paths.pathToBasicYulContract}`, `--combined-json`, `${json_args[i]}`];
const result = executeCommand(zksolcCommand, args);
it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1);
});
it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(ParserError: Expected identifier)/i);
});
asd
it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
}
});
@@ -4,8 +4,6 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use crate::solc::pipeline::Pipeline as SolcPipeline;
pub const MAIN_CODE: &str = r#" pub const MAIN_CODE: &str = r#"
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -51,7 +49,6 @@ fn default() {
sources, sources,
BTreeMap::new(), BTreeMap::new(),
None, None,
SolcPipeline::Yul,
revive_llvm_context::OptimizerSettings::cycles(), revive_llvm_context::OptimizerSettings::cycles(),
) )
.expect("Build failure"); .expect("Build failure");
-74
View File
@@ -5,8 +5,6 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use crate::solc::pipeline::Pipeline as SolcPipeline;
#[test] #[test]
fn yul() { fn yul() {
let source_code = r#" let source_code = r#"
@@ -27,7 +25,6 @@ contract Test {
sources, sources,
BTreeMap::new(), BTreeMap::new(),
None, None,
SolcPipeline::Yul,
revive_llvm_context::OptimizerSettings::cycles(), revive_llvm_context::OptimizerSettings::cycles(),
) )
.expect("Test failure"); .expect("Test failure");
@@ -45,75 +42,4 @@ contract Test {
.is_some(), .is_some(),
"Yul IR is missing" "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,
revive_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",
);
} }
+4 -16
View File
@@ -4,8 +4,6 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use crate::solc::pipeline::Pipeline as SolcPipeline;
pub const LIBRARY_TEST_SOURCE: &str = r#" pub const LIBRARY_TEST_SOURCE: &str = r#"
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
@@ -38,12 +36,8 @@ fn not_specified() {
let mut sources = BTreeMap::new(); let mut sources = BTreeMap::new();
sources.insert("test.sol".to_owned(), LIBRARY_TEST_SOURCE.to_owned()); sources.insert("test.sol".to_owned(), LIBRARY_TEST_SOURCE.to_owned());
for pipeline in [SolcPipeline::EVMLA, SolcPipeline::Yul] { let output =
let output = super::build_solidity_and_detect_missing_libraries( super::build_solidity_and_detect_missing_libraries(sources.clone(), BTreeMap::new())
sources.clone(),
BTreeMap::new(),
pipeline,
)
.expect("Test failure"); .expect("Test failure");
assert!( assert!(
output output
@@ -60,7 +54,6 @@ fn not_specified() {
.contains("test.sol:SimpleLibrary"), .contains("test.sol:SimpleLibrary"),
"Missing library not detected" "Missing library not detected"
); );
}
} }
#[test] #[test]
@@ -75,12 +68,8 @@ fn specified() {
.entry("SimpleLibrary".to_string()) .entry("SimpleLibrary".to_string())
.or_insert("0x00000000000000000000000000000000DEADBEEF".to_string()); .or_insert("0x00000000000000000000000000000000DEADBEEF".to_string());
for pipeline in [SolcPipeline::EVMLA, SolcPipeline::Yul] { let output =
let output = super::build_solidity_and_detect_missing_libraries( super::build_solidity_and_detect_missing_libraries(sources.clone(), libraries.clone())
sources.clone(),
libraries.clone(),
pipeline,
)
.expect("Test failure"); .expect("Test failure");
assert!( assert!(
output output
@@ -98,5 +87,4 @@ fn specified() {
.is_empty(), .is_empty(),
"The list of missing libraries must be empty" "The list of missing libraries must be empty"
); );
}
} }
+20 -161
View File
@@ -4,7 +4,6 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use crate::solc::pipeline::Pipeline as SolcPipeline;
use crate::warning::Warning; use crate::warning::Warning;
pub const ECRECOVER_TEST_SOURCE: &str = r#" pub const ECRECOVER_TEST_SOURCE: &str = r#"
@@ -30,7 +29,6 @@ fn ecrecover() {
ECRECOVER_TEST_SOURCE, ECRECOVER_TEST_SOURCE,
"Warning: It looks like you are using 'ecrecover' to validate a signature of a user account.", "Warning: It looks like you are using 'ecrecover' to validate a signature of a user account.",
BTreeMap::new(), BTreeMap::new(),
SolcPipeline::Yul,
false, false,
None, None,
).expect("Test failure") ).expect("Test failure")
@@ -44,7 +42,6 @@ fn ecrecover_suppressed() {
ECRECOVER_TEST_SOURCE, ECRECOVER_TEST_SOURCE,
"Warning: It looks like you are using 'ecrecover' to validate a signature of a user account.", "Warning: It looks like you are using 'ecrecover' to validate a signature of a user account.",
BTreeMap::new(), BTreeMap::new(),
SolcPipeline::Yul,
false, false,
Some(vec![Warning::EcRecover]), Some(vec![Warning::EcRecover]),
).expect("Test failure") ).expect("Test failure")
@@ -67,34 +64,33 @@ contract SendExample {
require(success, "Failed to send Ether"); require(success, "Failed to send Ether");
} }
} }
"#; "#;
pub const BALANCE_CALLS_MESSAGE: &str =
"Warning: It looks like you are using '<address payable>.send/transfer(<X>)'";
#[test] #[test]
fn send() { fn send() {
assert!( assert!(super::check_solidity_warning(
super::check_solidity_warning(
SEND_TEST_SOURCE, SEND_TEST_SOURCE,
"Warning: It looks like you are using '<address payable>.send/transfer(<X>)' without providing", BALANCE_CALLS_MESSAGE,
BTreeMap::new(), BTreeMap::new(),
SolcPipeline::Yul,
false, false,
None, None,
).expect("Test failure") )
); .expect("Test failure"));
} }
#[test] #[test]
fn send_suppressed() { fn send_suppressed() {
assert!( assert!(!super::check_solidity_warning(
!super::check_solidity_warning(
SEND_TEST_SOURCE, SEND_TEST_SOURCE,
"Warning: It looks like you are using '<address payable>.send/transfer(<X>)' without providing", BALANCE_CALLS_MESSAGE,
BTreeMap::new(), BTreeMap::new(),
SolcPipeline::Yul,
false, false,
Some(vec![Warning::SendTransfer]), Some(vec![Warning::SendTransfer]),
).expect("Test failure") )
); .expect("Test failure"));
} }
pub const TRANSFER_TEST_SOURCE: &str = r#" pub const TRANSFER_TEST_SOURCE: &str = r#"
@@ -116,30 +112,26 @@ contract TransferExample {
#[test] #[test]
fn transfer() { fn transfer() {
assert!( assert!(super::check_solidity_warning(
super::check_solidity_warning(
TRANSFER_TEST_SOURCE, TRANSFER_TEST_SOURCE,
"Warning: It looks like you are using '<address payable>.send/transfer(<X>)' without providing", BALANCE_CALLS_MESSAGE,
BTreeMap::new(), BTreeMap::new(),
SolcPipeline::Yul,
false, false,
None, None,
).expect("Test failure") )
); .expect("Test failure"));
} }
#[test] #[test]
fn transfer_suppressed() { fn transfer_suppressed() {
assert!( assert!(!super::check_solidity_warning(
!super::check_solidity_warning(
TRANSFER_TEST_SOURCE, TRANSFER_TEST_SOURCE,
"Warning: It looks like you are using '<address payable>.send/transfer(<X>)' without providing", BALANCE_CALLS_MESSAGE,
BTreeMap::new(), BTreeMap::new(),
SolcPipeline::Yul,
false, false,
Some(vec![Warning::SendTransfer]), Some(vec![Warning::SendTransfer]),
).expect("Test failure") )
); .expect("Test failure"));
} }
pub const EXTCODESIZE_TEST_SOURCE: &str = r#" pub const EXTCODESIZE_TEST_SOURCE: &str = r#"
@@ -163,7 +155,6 @@ fn extcodesize() {
EXTCODESIZE_TEST_SOURCE, EXTCODESIZE_TEST_SOURCE,
"Warning: Your code or one of its dependencies uses the 'extcodesize' instruction,", "Warning: Your code or one of its dependencies uses the 'extcodesize' instruction,",
BTreeMap::new(), BTreeMap::new(),
SolcPipeline::Yul,
false, false,
None, None,
) )
@@ -176,7 +167,6 @@ fn extcodesize_suppressed() {
EXTCODESIZE_TEST_SOURCE, EXTCODESIZE_TEST_SOURCE,
"Warning: Your code or one of its dependencies uses the 'extcodesize' instruction,", "Warning: Your code or one of its dependencies uses the 'extcodesize' instruction,",
BTreeMap::new(), BTreeMap::new(),
SolcPipeline::Yul,
false, false,
Some(vec![Warning::ExtCodeSize]), Some(vec![Warning::ExtCodeSize]),
) )
@@ -200,7 +190,6 @@ fn tx_origin() {
TX_ORIGIN_TEST_SOURCE, TX_ORIGIN_TEST_SOURCE,
"Warning: You are checking for 'tx.origin' in your code, which might lead to", "Warning: You are checking for 'tx.origin' in your code, which might lead to",
BTreeMap::new(), BTreeMap::new(),
SolcPipeline::Yul,
false, false,
None, None,
) )
@@ -213,7 +202,6 @@ fn tx_origin_suppressed() {
TX_ORIGIN_TEST_SOURCE, TX_ORIGIN_TEST_SOURCE,
"Warning: You are checking for 'tx.origin' in your code, which might lead to", "Warning: You are checking for 'tx.origin' in your code, which might lead to",
BTreeMap::new(), BTreeMap::new(),
SolcPipeline::Yul,
false, false,
Some(vec![Warning::TxOrigin]), Some(vec![Warning::TxOrigin]),
) )
@@ -244,7 +232,6 @@ fn tx_origin_assembly() {
TX_ORIGIN_ASSEMBLY_TEST_SOURCE, TX_ORIGIN_ASSEMBLY_TEST_SOURCE,
"Warning: You are checking for 'tx.origin' in your code, which might lead to", "Warning: You are checking for 'tx.origin' in your code, which might lead to",
BTreeMap::new(), BTreeMap::new(),
SolcPipeline::Yul,
false, false,
None, None,
) )
@@ -257,136 +244,8 @@ fn tx_origin_assembly_suppressed() {
TX_ORIGIN_ASSEMBLY_TEST_SOURCE, TX_ORIGIN_ASSEMBLY_TEST_SOURCE,
"Warning: You are checking for 'tx.origin' in your code, which might lead to", "Warning: You are checking for 'tx.origin' in your code, which might lead to",
BTreeMap::new(), BTreeMap::new(),
SolcPipeline::Yul,
false, false,
Some(vec![Warning::TxOrigin]), Some(vec![Warning::TxOrigin]),
) )
.expect("Test failure")); .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"));
}
-5
View File
@@ -4,8 +4,6 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use crate::solc::pipeline::Pipeline as SolcPipeline;
pub const SOURCE_CODE: &str = r#" pub const SOURCE_CODE: &str = r#"
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@@ -54,7 +52,6 @@ fn optimizer() {
sources.clone(), sources.clone(),
BTreeMap::new(), BTreeMap::new(),
None, None,
SolcPipeline::Yul,
revive_llvm_context::OptimizerSettings::none(), revive_llvm_context::OptimizerSettings::none(),
) )
.expect("Build failure"); .expect("Build failure");
@@ -62,7 +59,6 @@ fn optimizer() {
sources.clone(), sources.clone(),
BTreeMap::new(), BTreeMap::new(),
None, None,
SolcPipeline::Yul,
revive_llvm_context::OptimizerSettings::cycles(), revive_llvm_context::OptimizerSettings::cycles(),
) )
.expect("Build failure"); .expect("Build failure");
@@ -70,7 +66,6 @@ fn optimizer() {
sources, sources,
BTreeMap::new(), BTreeMap::new(),
None, None,
SolcPipeline::Yul,
revive_llvm_context::OptimizerSettings::size(), revive_llvm_context::OptimizerSettings::size(),
) )
.expect("Build failure"); .expect("Build failure");

Some files were not shown because too many files have changed in this diff Show More