Compare commits

..

2 Commits

Author SHA1 Message Date
pgherveou 20a3b30296 wip 2025-04-28 16:39:59 +02:00
pgherveou f44b2485bd lint 2025-04-24 16:34:06 +02:00
69 changed files with 5170 additions and 8096 deletions
+2 -2
View File
@@ -10,8 +10,8 @@ rustflags = [
"-Clink-arg=-sWASM_ASYNC_COMPILATION=0", "-Clink-arg=-sWASM_ASYNC_COMPILATION=0",
"-Clink-arg=-sDYNAMIC_EXECUTION=0", "-Clink-arg=-sDYNAMIC_EXECUTION=0",
"-Clink-arg=-sALLOW_TABLE_GROWTH=1", "-Clink-arg=-sALLOW_TABLE_GROWTH=1",
"-Clink-arg=--js-library=js/emscripten/embed/soljson_interface.js", "-Clink-arg=--js-library=js/embed/soljson_interface.js",
"-Clink-arg=--pre-js=js/emscripten/embed/pre.js", "-Clink-arg=--pre-js=js/embed/pre.js",
"-Clink-arg=-sSTACK_SIZE=128kb", "-Clink-arg=-sSTACK_SIZE=128kb",
"-Clink-arg=-sNODEJS_CATCH_EXIT=0" "-Clink-arg=-sNODEJS_CATCH_EXIT=0"
] ]
-1
View File
@@ -5,7 +5,6 @@ inputs:
required: false required: false
default: "3.1.64" default: "3.1.64"
runs: runs:
using: "composite" using: "composite"
steps: steps:
+49
View File
@@ -0,0 +1,49 @@
name: NPM Release
on:
release:
types: [released]
env:
CI: true
jobs:
publish:
name: Build & Publish to NPM
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22.x
- name: Install
run: yarn install --immutable
- name: update-resolc
run: yarn update-resolc
- name: Build
run: yarn build
- name: Set version
run: npm version --no-git-tag-version ${{github.event.release.tag_name}}
- name: npm pack
run: npm pack
- uses: actions/upload-artifact@v4
with:
name: package
path: 'parity-revive-*.tgz'
- uses: octokit/request-action@bbedc70b1981e610d89f1f8de88311a1fc02fb83
with:
route: POST /repos/paritytech/npm_publish_automation/actions/workflows/publish.yml/dispatches
ref: main
inputs: '${{ format(''{{ "repo": "{0}", "run_id": "{1}" }}'', github.repository, github.run_id) }}'
env:
GITHUB_TOKEN: ${{ secrets.NPM_PUBLISH_AUTOMATION_TOKEN }}
+9 -6
View File
@@ -37,7 +37,15 @@ jobs:
build: build:
strategy: strategy:
matrix: matrix:
target: [x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl, wasm32-unknown-emscripten, aarch64-apple-darwin, x86_64-apple-darwin, x86_64-pc-windows-msvc] target:
[
x86_64-unknown-linux-gnu,
x86_64-unknown-linux-musl,
wasm32-unknown-emscripten,
aarch64-apple-darwin,
x86_64-apple-darwin,
x86_64-pc-windows-msvc,
]
include: include:
- target: x86_64-unknown-linux-gnu - target: x86_64-unknown-linux-gnu
builder-arg: gnu builder-arg: gnu
@@ -87,11 +95,6 @@ jobs:
run: | run: |
brew install ninja brew install ninja
- name: Install Dependencies
if: ${{ matrix.host == 'windows' }}
run: |
choco install ninja
- name: Install LLVM Builder - name: Install LLVM Builder
run: | run: |
cargo install --path crates/llvm-builder cargo install --path crates/llvm-builder
-46
View File
@@ -286,49 +286,3 @@ jobs:
resolc.wasm resolc.wasm
resolc_web.js resolc_web.js
checksums.txt checksums.txt
npm-release:
needs: [create-release]
runs-on: macos-14
environment: tags
steps:
- uses: actions/checkout@v4
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
merge-multiple: true
- name: Set Up Node.js
uses: actions/setup-node@v3
with:
node-version: "20"
- run: npm ci -w js/resolc
- name: Build
run: |
cp -f resolc.{wasm,js} js/resolc/src/resolc
npm -w js/resolc run build
- name: Set version
run: |
VERSION="${{ github.event.release.tag_name }}"
STRIPPED_VERSION="${VERSION#v}"
npm -w js/resolc version --no-git-tag-version "$STRIPPED_VERSION"
- name: npm pack
run: npm -w js/resolc pack
- uses: actions/upload-artifact@v4
with:
name: npm_package
path: "parity-resolc-*.tgz"
- uses: octokit/request-action@bbedc70b1981e610d89f1f8de88311a1fc02fb83
with:
route: POST /repos/paritytech/npm_publish_automation/actions/workflows/publish.yml/dispatches
ref: main
inputs: '${{ format(''{{ "artifact_name": "npm_package", "repo": "{0}", "run_id": "{1}" }}'', github.repository, github.run_id) }}'
env:
GITHUB_TOKEN: ${{ secrets.NPM_PUBLISH_AUTOMATION_TOKEN }}
+3 -3
View File
@@ -4,9 +4,9 @@ on:
branches: ["main"] branches: ["main"]
types: [opened, synchronize] types: [opened, synchronize]
paths: paths:
- 'LLVM.lock' - "LLVM.lock"
- 'crates/llvm-builder/**' - "crates/llvm-builder/**"
- '.github/workflows/test-llvm-builder.yml' - ".github/workflows/test-llvm-builder.yml"
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+7 -12
View File
@@ -86,18 +86,13 @@ jobs:
- name: Install Node Packages - name: Install Node Packages
run: npm install run: npm install
- name: Test emscripten - name: Run Playwright tests
run: |
cd js
npx playwright install --with-deps
npx playwright test
- name: Test revive
run: | run: |
echo "Running tests for ${{ matrix.os }}" echo "Running tests for ${{ matrix.os }}"
npm run test:wasm npm run test:wasm
- name: Test @parity/resolc
run: |
echo "Running tests for ${{ matrix.os }}"
npm run -w js/resolc test
- name: Run Playwright tests
run: |
cd js/emscripten
npx playwright install --with-deps
npx playwright test
+1
View File
@@ -12,6 +12,7 @@ target-llvm
node_modules node_modules
artifacts artifacts
tmp tmp
package-lock.json
/*.html /*.html
/build /build
soljson.js soljson.js
-6
View File
@@ -1,6 +0,0 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": false,
"singleQuote": true
}
+27 -8
View File
@@ -6,16 +6,9 @@ This is a development pre-release.
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee` Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
## v0.1.0
This is a development pre-release.
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
### Added ### Added
### Changed ### Changed
- By default, heavy size optimizations are applied.
### Fixed ### Fixed
@@ -26,13 +19,16 @@ This is a development pre-release.
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee` Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
### Added ### Added
- The `revive-runner` helper utility binary which helps to run contracts locally without a blockchain node. - The `revive-runner` helper utility binary which helps to run contracts locally without a blockchain node.
- Allow configuration of the EVM heap memory size and stack size via CLI flags and JSON input settings. - Allow configuration of the EVM heap memory size and stack size via CLI flags and JSON input settings.
### Changed ### Changed
- The default PVM stack memory size was increased from 16kb to 32kb. - The default PVM stack memory size was increased from 16kb to 32kb.
### Fixed ### Fixed
- Constructors avoid storing zero sized immutable data on exit. - Constructors avoid storing zero sized immutable data on exit.
## v0.1.0-dev.13 ## v0.1.0-dev.13
@@ -42,16 +38,19 @@ This is a development pre-release.
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee` Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
### Added ### Added
- Support for solc v0.8.29 - Support for solc v0.8.29
- Decouples the solc JSON-input-output type definitions from the Solidity fronted and expose them via a dedicated crate. - Decouples the solc JSON-input-output type definitions from the Solidity fronted and expose them via a dedicated crate.
- `--supported-solc-versions` for `resolc` binary to return a `semver` range of supported `solc` versions. - `--supported-solc-versions` for `resolc` binary to return a `semver` range of supported `solc` versions.
- Support for passing LLVM command line options via the prcoess input or providing one or more `--llvm-arg='..'` resolc CLI flag. This allows more fine-grained control over the LLVM backend configuration. - Support for passing LLVM command line options via the prcoess input or providing one or more `--llvm-arg='..'` resolc CLI flag. This allows more fine-grained control over the LLVM backend configuration.
### Changed ### Changed
- Storage keys and values are big endian. This was a pre-mature optimization because for the contract itself it this is a no-op and thus not observable. However we should consider the storage layout as part of the contract ABI. The endianness of transient storage values are still kept as-is. - Storage keys and values are big endian. This was a pre-mature optimization because for the contract itself it this is a no-op and thus not observable. However we should consider the storage layout as part of the contract ABI. The endianness of transient storage values are still kept as-is.
- Running `resolc` using webkit is no longer supported. - Running `resolc` using webkit is no longer supported.
### Fixed ### Fixed
- A missing byte swap for the create2 salt value. - A missing byte swap for the create2 salt value.
## v0.1.0-dev.12 ## v0.1.0-dev.12
@@ -61,10 +60,12 @@ This is a development pre-release.
Supported `polkadot-sdk` rev: `21f6f0705e53c15aa2b8a5706b208200447774a9` Supported `polkadot-sdk` rev: `21f6f0705e53c15aa2b8a5706b208200447774a9`
### Added ### Added
- Per file output selection for `--standard-json` mode. - Per file output selection for `--standard-json` mode.
- The `ir` output selection option for `--standard-json` mode. - The `ir` output selection option for `--standard-json` mode.
### Changed ### Changed
- Improved code size: Large contracts compile to smaller code blobs when enabling aggressive size optimizations (`-Oz`). - Improved code size: Large contracts compile to smaller code blobs when enabling aggressive size optimizations (`-Oz`).
### Fixed ### Fixed
@@ -80,6 +81,7 @@ Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
### Changed ### Changed
### Fixed ### Fixed
- A bug causing incorrect loads from the emulated EVM linear memory. - A bug causing incorrect loads from the emulated EVM linear memory.
- A missing integer truncate after switching to 64bit. - A missing integer truncate after switching to 64bit.
@@ -90,10 +92,12 @@ This is a development pre-release.
Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50` Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
### Added ### Added
- Support for the `coinbase` opcode. - Support for the `coinbase` opcode.
- The resolc web JS version. - The resolc web JS version.
### Changed ### Changed
- Missing the `--overwrite` flag emits an error instead of a warning. - Missing the `--overwrite` flag emits an error instead of a warning.
- The `resolc` executable prints the help by default. - The `resolc` executable prints the help by default.
- Removed support for legacy EVM assembly (EVMLA) translation. - Removed support for legacy EVM assembly (EVMLA) translation.
@@ -103,6 +107,7 @@ Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
If detected, the re-entrant call flag is not set and 0 deposit limit is endowed. If detected, the re-entrant call flag is not set and 0 deposit limit is endowed.
### Fixed ### Fixed
- Solidity: Add the solc `--libraries` files to sources. - Solidity: Add the solc `--libraries` files to sources.
- A data race in tests. - A data race in tests.
- Fix `broken pipe` errors. - Fix `broken pipe` errors.
@@ -116,9 +121,11 @@ This is a development pre-release.
### Added ### Added
### Changed ### Changed
- Syscalls with more than 6 arguments now pack them into registers. - Syscalls with more than 6 arguments now pack them into registers.
### Fixed ### Fixed
- Remove reloading of the resolc.js file (fix issue with relative path in web worker) - Remove reloading of the resolc.js file (fix issue with relative path in web worker)
## v0.1.0-dev.8 ## v0.1.0-dev.8
@@ -126,15 +133,18 @@ This is a development pre-release.
This is a development pre-release. This is a development pre-release.
### Added ### Added
- The `revive-llvm-builder` crate with the `revive-llvm` helper utility for streamlined management of the LLVM framework dependency. - The `revive-llvm-builder` crate with the `revive-llvm` helper utility for streamlined management of the LLVM framework dependency.
- Initial support for running `resolc` in the browser. - Initial support for running `resolc` in the browser.
### Changed ### Changed
- Suported contracts runtime is polkadot-sdk git version `d62a90c8c729acd98c7e9a5cab9803b8b211ffc5`. - Suported contracts runtime is polkadot-sdk git version `d62a90c8c729acd98c7e9a5cab9803b8b211ffc5`.
- The minimum supported Rust version is `1.81.0`. - The minimum supported Rust version is `1.81.0`.
- Error out early instead of invoking `solc` with invalid base or include path flags. - Error out early instead of invoking `solc` with invalid base or include path flags.
### Fixed ### Fixed
- Decouple the LLVM target dependency from the LLVM host dependency. - Decouple the LLVM target dependency from the LLVM host dependency.
- Do not error out if no files and no errors were produced. This aligns resolc closer to solc. - Do not error out if no files and no errors were produced. This aligns resolc closer to solc.
- Fixes input normalization in the Wasm version. - Fixes input normalization in the Wasm version.
@@ -144,17 +154,20 @@ This is a development pre-release.
This is a development pre-release. This is a development pre-release.
### Added ### Added
- Implement the `GASPRICE` opcode. - Implement the `GASPRICE` opcode.
- Implement the `BASEFEE` opcode. - Implement the `BASEFEE` opcode.
- Implement the `GASLIMIT` opcode. - Implement the `GASLIMIT` opcode.
### Changed ### Changed
- The `GAS` opcode now returns the remaining `ref_time`. - The `GAS` opcode now returns the remaining `ref_time`.
- Contracts can now be supplied call data input of arbitrary size. - Contracts can now be supplied call data input of arbitrary size.
- Some syscalls now return the value in a register, slightly improving emitted contract code. - Some syscalls now return the value in a register, slightly improving emitted contract code.
- Calls forward maximum weight limits instead of 0, anticipating a change in polkadot-sdk where weight limits of 0 no longer interprets as uncapped limit. - Calls forward maximum weight limits instead of 0, anticipating a change in polkadot-sdk where weight limits of 0 no longer interprets as uncapped limit.
### Fixed ### Fixed
- A linker bug which was preventing certain contracts from linking with the PVM linker. - A linker bug which was preventing certain contracts from linking with the PVM linker.
- JS: Fix encoding conversion from JS string (UTF-16) to UTF-8. - JS: Fix encoding conversion from JS string (UTF-16) to UTF-8.
- The git commit hash slug is always displayed in the version string. - The git commit hash slug is always displayed in the version string.
@@ -164,6 +177,7 @@ This is a development pre-release.
This is a development pre-release. This is a development pre-release.
# Added # Added
- Implement the `BLOCKHASH` opcode. - Implement the `BLOCKHASH` opcode.
- Implement delegate calls. - Implement delegate calls.
- Implement the `GASPRICE` opcode. Currently hard-coded to return `1`. - Implement the `GASPRICE` opcode. Currently hard-coded to return `1`.
@@ -171,21 +185,24 @@ This is a development pre-release.
- Initial support for emitting debug info (opt in via the `-g` flag) - Initial support for emitting debug info (opt in via the `-g` flag)
# Changed # Changed
- resolc now emits 64bit PolkaVM blobs, reducing contract code size and execution time. - resolc now emits 64bit PolkaVM blobs, reducing contract code size and execution time.
- The RISC-V bit-manipulation target feature (`zbb`) is enabled. - The RISC-V bit-manipulation target feature (`zbb`) is enabled.
# Fixed # Fixed
- Compilation to Wasm (for usage in node and web browsers)
- Compilation to Wasm (for usage in node and web browsers)
## v0.1.0-dev.5 ## v0.1.0-dev.5
This is development pre-release. This is development pre-release.
# Added # Added
- Implement the `CODESIZE` and `EXTCODESIZE` opcodes. - Implement the `CODESIZE` and `EXTCODESIZE` opcodes.
# Changed # Changed
- Include the full revive version in the contract metadata. - Include the full revive version in the contract metadata.
# Fixed # Fixed
@@ -195,9 +212,11 @@ This is development pre-release.
This is development pre-release. This is development pre-release.
# Added # Added
- Support the `ORIGIN` opcode. - Support the `ORIGIN` opcode.
# Changed # Changed
- Update polkavm to `v0.14.0`. - Update polkavm to `v0.14.0`.
- Enable the `a`, `fast-unaligned-access` and `xtheadcondmov` LLVM target features, decreasing the code size for some contracts. - Enable the `a`, `fast-unaligned-access` and `xtheadcondmov` LLVM target features, decreasing the code size for some contracts.
+4 -1
View File
@@ -92,4 +92,7 @@ clean:
rm -rf node_modules ; \ rm -rf node_modules ; \
rm -rf crates/solidity/src/tests/cli-tests/artifacts ; \ rm -rf crates/solidity/src/tests/cli-tests/artifacts ; \
cargo uninstall revive-solidity ; \ cargo uninstall revive-solidity ; \
cargo uninstall revive-llvm-builder ; cargo uninstall revive-llvm-builder ; \
rm -f package-lock.json ; \
rm -rf js/dist ; \
rm -f js/src/resolc.{wasm,js}
+5 -1
View File
@@ -14,6 +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. Please consult [the documentation](https://contracts.polkadot.io/revive_compiler/installation) for installation instructions.
## Building from source ## Building from source
@@ -30,11 +31,13 @@ Building revive requires a [stable Rust installation](https://rustup.rs/) and a
Download the [latest LLVM build](https://github.com/paritytech/revive/releases?q=LLVM+binaries+release&expanded=true) from our releases. 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: > **MacOS** users need to clear the `downloaded` attribute from all binaries after extracting the archive:
>
> ```sh > ```sh
> xattr -rc </path/to/the/extracted/archive>/target-llvm/gnu/target-final/bin/* > xattr -rc </path/to/the/extracted/archive>/target-llvm/gnu/target-final/bin/*
> ``` > ```
After extracting the archive, point `$LLVM_SYS_181_PREFIX` to it: After extracting the archive, point `$LLVM_SYS_181_PREFIX` to it:
```sh ```sh
export LLVM_SYS_181_PREFIX=</path/to/the/extracted/archive>/target-llvm/gnu/target-final export LLVM_SYS_181_PREFIX=</path/to/the/extracted/archive>/target-llvm/gnu/target-final
``` ```
@@ -44,7 +47,7 @@ export LLVM_SYS_181_PREFIX=</path/to/the/extracted/archive>/target-llvm/gnu/targ
<details> <details>
<summary>Building from source</summary> <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. 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:
@@ -97,6 +100,7 @@ Please consult the [Makefile](Makefile) targets to learn how to run tests and be
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. See the [relevant section in our documentation](https://contracts.polkadot.io/revive_compiler/architecture) to learn more about how the compiler works.
[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` (the project started as a fork of the era compiler).
+15 -15
View File
@@ -16,13 +16,13 @@
### Baseline ### Baseline
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:--------|:-------------------------|:-------------------------------- | | :------ | :------------------------ | :------------------------------- |
| **`0`** | `10.08 us` (✅ **1.00x**) | `10.32 us` (✅ **1.02x slower**) | | **`0`** | `10.08 us` (✅ **1.00x**) | `10.32 us` (✅ **1.02x slower**) |
### OddPorduct ### OddPorduct
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:-------------|:--------------------------|:-------------------------------- | | :----------- | :------------------------- | :------------------------------- |
| **`10000`** | `3.60 ms` (✅ **1.00x**) | `1.57 ms` (🚀 **2.28x faster**) | | **`10000`** | `3.60 ms` (✅ **1.00x**) | `1.57 ms` (🚀 **2.28x faster**) |
| **`100000`** | `34.72 ms` (✅ **1.00x**) | `14.82 ms` (🚀 **2.34x faster**) | | **`100000`** | `34.72 ms` (✅ **1.00x**) | `14.82 ms` (🚀 **2.34x faster**) |
| **`300000`** | `105.01 ms` (✅ **1.00x**) | `44.11 ms` (🚀 **2.38x faster**) | | **`300000`** | `105.01 ms` (✅ **1.00x**) | `44.11 ms` (🚀 **2.38x faster**) |
@@ -30,7 +30,7 @@
### TriangleNumber ### TriangleNumber
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:-------------|:-------------------------|:-------------------------------- | | :----------- | :------------------------ | :------------------------------- |
| **`10000`** | `2.43 ms` (✅ **1.00x**) | `1.12 ms` (🚀 **2.17x faster**) | | **`10000`** | `2.43 ms` (✅ **1.00x**) | `1.12 ms` (🚀 **2.17x faster**) |
| **`100000`** | `24.20 ms` (✅ **1.00x**) | `10.86 ms` (🚀 **2.23x faster**) | | **`100000`** | `24.20 ms` (✅ **1.00x**) | `10.86 ms` (🚀 **2.23x faster**) |
| **`360000`** | `88.69 ms` (✅ **1.00x**) | `38.46 ms` (🚀 **2.31x faster**) | | **`360000`** | `88.69 ms` (✅ **1.00x**) | `38.46 ms` (🚀 **2.31x faster**) |
@@ -38,7 +38,7 @@
### FibonacciRecursive ### FibonacciRecursive
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:---------|:--------------------------|:--------------------------------- | | :------- | :------------------------- | :-------------------------------- |
| **`12`** | `144.17 us` (✅ **1.00x**) | `150.85 us` (✅ **1.05x slower**) | | **`12`** | `144.17 us` (✅ **1.00x**) | `150.85 us` (✅ **1.05x slower**) |
| **`16`** | `938.71 us` (✅ **1.00x**) | `922.11 us` (✅ **1.02x faster**) | | **`16`** | `938.71 us` (✅ **1.00x**) | `922.11 us` (✅ **1.02x faster**) |
| **`20`** | `6.54 ms` (✅ **1.00x**) | `6.20 ms` (✅ **1.05x faster**) | | **`20`** | `6.54 ms` (✅ **1.00x**) | `6.20 ms` (✅ **1.05x faster**) |
@@ -47,27 +47,27 @@
### FibonacciIterative ### FibonacciIterative
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:----------|:-------------------------|:-------------------------------- | | :-------- | :------------------------ | :------------------------------- |
| **`64`** | `23.00 us` (✅ **1.00x**) | `31.88 us` (❌ *1.39x slower*) | | **`64`** | `23.00 us` (✅ **1.00x**) | `31.88 us` (❌ _1.39x slower_) |
| **`128`** | `35.28 us` (✅ **1.00x**) | `42.43 us` (❌ *1.20x slower*) | | **`128`** | `35.28 us` (✅ **1.00x**) | `42.43 us` (❌ _1.20x slower_) |
| **`256`** | `60.12 us` (✅ **1.00x**) | `61.20 us` (✅ **1.02x slower**) | | **`256`** | `60.12 us` (✅ **1.00x**) | `61.20 us` (✅ **1.02x slower**) |
### FibonacciBinet ### FibonacciBinet
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:----------|:-------------------------|:-------------------------------- | | :-------- | :------------------------ | :----------------------------- |
| **`64`** | `23.01 us` (✅ **1.00x**) | `47.74 us` (❌ *2.07x slower*) | | **`64`** | `23.01 us` (✅ **1.00x**) | `47.74 us` (❌ _2.07x slower_) |
| **`128`** | `25.44 us` (✅ **1.00x**) | `49.67 us` (❌ *1.95x slower*) | | **`128`** | `25.44 us` (✅ **1.00x**) | `49.67 us` (❌ _1.95x slower_) |
| **`256`** | `28.66 us` (✅ **1.00x**) | `53.01 us` (❌ *1.85x slower*) | | **`256`** | `28.66 us` (✅ **1.00x**) | `53.01 us` (❌ _1.85x slower_) |
### SHA1 ### SHA1
| | `EVM` | `PVMInterpreter` | | | `EVM` | `PVMInterpreter` |
|:----------|:--------------------------|:--------------------------------- | | :-------- | :------------------------- | :------------------------------ |
| **`1`** | `135.87 us` (✅ **1.00x**) | `243.75 us` (❌ *1.79x slower*) | | **`1`** | `135.87 us` (✅ **1.00x**) | `243.75 us` (❌ _1.79x slower_) |
| **`64`** | `258.45 us` (✅ **1.00x**) | `355.70 us` (❌ *1.38x slower*) | | **`64`** | `258.45 us` (✅ **1.00x**) | `355.70 us` (❌ _1.38x slower_) |
| **`512`** | `1.10 ms` (✅ **1.00x**) | `1.09 ms` (✅ **1.01x faster**) | | **`512`** | `1.10 ms` (✅ **1.00x**) | `1.09 ms` (✅ **1.01x faster**) |
--- ---
Made with [criterion-table](https://github.com/nu11ptr/criterion-table)
Made with [criterion-table](https://github.com/nu11ptr/criterion-table)
+23 -14
View File
@@ -6,6 +6,7 @@ Parity fork of the [Matter Labs zksync LLVM builder](https://github.com/matter-l
The LLVM compiler framework for revive must be built with our tool called `revive-llvm`. The LLVM compiler framework for revive must be built with our tool called `revive-llvm`.
This is because the revive compiler has requirements not fullfilled in upstream builds: This is because the revive compiler has requirements not fullfilled in upstream builds:
- Special builds for compiling the frontend into statically linked ELF binaries and also Wasm executables - Special builds for compiling the frontend into statically linked ELF binaries and also Wasm executables
- The RISC-V target (the PolkaVM target) - The RISC-V target (the PolkaVM target)
- The compiler-rt builtins for the PolkaVM target - The compiler-rt builtins for the PolkaVM target
@@ -17,20 +18,23 @@ Obtain a compatible build for your host platform from the release section of thi
<details> <details>
<summary>1. Install the system prerequisites.</summary> <summary>1. Install the system prerequisites.</summary>
* Linux (Debian): - Linux (Debian):
Install the following packages: Install the following packages:
```shell ```shell
apt install cmake ninja-build curl git libssl-dev pkg-config clang lld apt install cmake ninja-build curl git libssl-dev pkg-config clang lld
``` ```
* Linux (Arch):
- Linux (Arch):
Install the following packages: Install the following packages:
```shell ```shell
pacman -Syu which cmake ninja curl git pkg-config clang lld pacman -Syu which cmake ninja curl git pkg-config clang lld
``` ```
* MacOS: - MacOS:
* Install the [HomeBrew](https://brew.sh) package manager. * Install the [HomeBrew](https://brew.sh) package manager.
* Install the following packages: * Install the following packages:
@@ -40,24 +44,27 @@ Obtain a compatible build for your host platform from the release section of thi
``` ```
* Install your choice of a recent LLVM/[Clang](https://clang.llvm.org) compiler, e.g. via [Xcode](https://developer.apple.com/xcode/), [Apples Command Line Tools](https://developer.apple.com/library/archive/technotes/tn2339/_index.html), or your preferred package manager. * Install your choice of a recent LLVM/[Clang](https://clang.llvm.org) compiler, e.g. via [Xcode](https://developer.apple.com/xcode/), [Apples Command Line Tools](https://developer.apple.com/library/archive/technotes/tn2339/_index.html), or your preferred package manager.
</details>
</details>
<details> <details>
<summary>2. Install Rust.</summary> <summary>2. Install Rust.</summary>
* Follow the latest [official instructions](https://www.rust-lang.org/tools/install: - Follow the latest [official instructions](https://www.rust-lang.org/tools/install:
```shell `shell
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
. ${HOME}/.cargo/env . ${HOME}/.cargo/env
``` `
> Currently we are not pinned to any specific version of Rust, so just install the latest stable build for your platform. > Currently we are not pinned to any specific version of Rust, so just install the latest stable build for your platform.
</details>
</details>
<details> <details>
<summary>3. Install the revive LLVM framework builder.</summary> <summary>3. Install the revive LLVM framework builder.</summary>
* Install the builder using `cargo`: - Install the builder using `cargo`:
```shell ```shell
cargo install --git https://github.com/paritytech/revive-llvm-builder --force --locked cargo install --git https://github.com/paritytech/revive-llvm-builder --force --locked
``` ```
@@ -69,7 +76,7 @@ Obtain a compatible build for your host platform from the release section of thi
<details> <details>
<summary>4. (Optional) Create the `LLVM.lock` file.</summary> <summary>4. (Optional) Create the `LLVM.lock` file.</summary>
* The `LLVM.lock` dictates the LLVM source tree being used. - The `LLVM.lock` dictates the LLVM source tree being used.
A default `./LLVM.lock` pointing to the release used for development is already provided. A default `./LLVM.lock` pointing to the release used for development is already provided.
</details> </details>
@@ -77,7 +84,7 @@ Obtain a compatible build for your host platform from the release section of thi
<details> <details>
<summary>5. Build LLVM.</summary> <summary>5. Build LLVM.</summary>
* Clone and build the LLVM framework using the `revive-llvm` tool. - Clone and build the LLVM framework using the `revive-llvm` tool.
The clang and lld projects are required for the `resolc` Solidity frontend executable; they are enabled by default. LLVM assertions are also enabled by default. The clang and lld projects are required for the `resolc` Solidity frontend executable; they are enabled by default. LLVM assertions are also enabled by default.
@@ -96,6 +103,7 @@ Obtain a compatible build for your host platform from the release section of thi
## Supported target architectures ## Supported target architectures
The following target platforms are supported: The following target platforms are supported:
- Linux GNU (x86) - Linux GNU (x86)
- Linux MUSL (x86) - Linux MUSL (x86)
- MacOS (aarch64) - MacOS (aarch64)
@@ -105,11 +113,12 @@ The following target platforms are supported:
<details> <details>
<summary>Building for MUSL</summary> <summary>Building for MUSL</summary>
* Via a musl build we can build revive into fully static ELF binaries. - Via a musl build we can build revive into fully static ELF binaries.
Which is desirable for reproducible Solidity contracts builds. Which is desirable for reproducible Solidity contracts builds.
The resulting binary is also very portable, akin to the`solc` frontend binary distribution. The resulting binary is also very portable, akin to the`solc` frontend binary distribution.
Clone and build the LLVM framework using the `revive-llvm` tool: Clone and build the LLVM framework using the `revive-llvm` tool:
```shell ```shell
revive-llvm --target-env musl clone revive-llvm --target-env musl clone
revive-llvm --target-env musl build --enable-assertions --llvm-projects clang --llvm-projects lld revive-llvm --target-env musl build --enable-assertions --llvm-projects clang --llvm-projects lld
@@ -120,13 +129,13 @@ The following target platforms are supported:
<details> <details>
<summary>Building for Emscripten</summary> <summary>Building for Emscripten</summary>
* Via an emsdk build we can run revive in the browser and on node.js. - Via an emsdk build we can run revive in the browser and on node.js.
Clone and build the LLVM framework using the `revive-llvm` tool: Clone and build the LLVM framework using the `revive-llvm` tool:
```shell ```shell
revive-llvm --target-env emscripten clone revive-llvm --target-env emscripten clone
revive-llvm --target-env emscripten build --enable-assertions --llvm-projects clang --llvm-projects lld revive-llvm --target-env emscripten build --enable-assertions --llvm-projects clang --llvm-projects lld
``` ```
</details> </details>
+7 -1
View File
@@ -117,7 +117,13 @@ pub fn build(
log::info!("building compiler-rt for rv64emac"); log::info!("building compiler-rt for rv64emac");
crate::utils::check_presence("cmake")?; crate::utils::check_presence("cmake")?;
let generator = if cfg!(target_os = "windows") {
"Visual Studio 17 2022"
} else {
crate::utils::check_presence("ninja")?; crate::utils::check_presence("ninja")?;
"Ninja"
};
let llvm_module_compiler_rt = crate::LLVMPath::llvm_module_compiler_rt()?; let llvm_module_compiler_rt = crate::LLVMPath::llvm_module_compiler_rt()?;
let llvm_compiler_rt_build = crate::LLVMPath::llvm_build_compiler_rt()?; let llvm_compiler_rt_build = crate::LLVMPath::llvm_build_compiler_rt()?;
@@ -130,7 +136,7 @@ pub fn build(
"-B", "-B",
llvm_compiler_rt_build.to_string_lossy().as_ref(), llvm_compiler_rt_build.to_string_lossy().as_ref(),
"-G", "-G",
"Ninja", generator,
]) ])
.args(CMAKE_STATIC_ARGS) .args(CMAKE_STATIC_ARGS)
.args(cmake_dynamic_args(build_type, target_env)?) .args(cmake_dynamic_args(build_type, target_env)?)
@@ -234,7 +234,7 @@ impl TryFrom<&SolcStandardJsonInputSettingsOptimizer> for Settings {
fn try_from(value: &SolcStandardJsonInputSettingsOptimizer) -> Result<Self, Self::Error> { fn try_from(value: &SolcStandardJsonInputSettingsOptimizer) -> Result<Self, Self::Error> {
let mut result = match value.mode { let mut result = match value.mode {
Some(mode) => Self::try_from_cli(mode)?, Some(mode) => Self::try_from_cli(mode)?,
None => Self::size(), None => Self::cycles(),
}; };
if value.fallback_to_optimizing_for_size.unwrap_or_default() { if value.fallback_to_optimizing_for_size.unwrap_or_default() {
result.enable_fallback_to_size(); result.enable_fallback_to_size();
+1
View File
@@ -24,3 +24,4 @@ Set the `RUST_LOG` environment varibale to the `trace` level to see the full Pol
```bash ```bash
RUST_LOG=trace revive-runner -f mycontract.pvm -c a9059cbb000000000000000000000000f24ff3a9cf04c71dbc94d0b566f7a27b94566cac0000000000000000000000000000000000000000000000000000000000000000 RUST_LOG=trace revive-runner -f mycontract.pvm -c a9059cbb000000000000000000000000f24ff3a9cf04c71dbc94d0b566f7a27b94566cac0000000000000000000000000000000000000000000000000000000000000000
```
+1 -1
View File
@@ -129,7 +129,7 @@ fn main_inner() -> anyhow::Result<()> {
let mut optimizer_settings = match arguments.optimization { let mut optimizer_settings = match arguments.optimization {
Some(mode) => revive_llvm_context::OptimizerSettings::try_from_cli(mode)?, Some(mode) => revive_llvm_context::OptimizerSettings::try_from_cli(mode)?,
None => revive_llvm_context::OptimizerSettings::size(), None => revive_llvm_context::OptimizerSettings::cycles(),
}; };
if arguments.fallback_to_optimizing_for_size { if arguments.fallback_to_optimizing_for_size {
optimizer_settings.enable_fallback_to_size(); optimizer_settings.enable_fallback_to_size();
@@ -90,14 +90,10 @@
}, },
"outputSelection": { "outputSelection": {
"*": { "*": {
"": [ "": ["ast"]
"ast"
]
} }
}, },
"evmVersion": "berlin", "evmVersion": "berlin",
"remappings": [ "remappings": ["forge-std/=lib/forge-std/src/"]
"forge-std/=lib/forge-std/src/"
]
} }
} }
@@ -1,33 +1,33 @@
import * as path from 'path' import * as path from "path";
const outputDir = 'artifacts' const outputDir = "artifacts";
const binExtension = ':C.pvm' const binExtension = ":C.pvm";
const asmExtension = ':C.pvmasm' const asmExtension = ":C.pvmasm";
const llvmExtension = '.ll' const llvmExtension = ".ll";
const contractSolFilename = 'contract.sol' const contractSolFilename = "contract.sol";
const contractYulFilename = 'contract.yul' const contractYulFilename = "contract.yul";
const contractOptimizedLLVMFilename = contractSolFilename + '.C.optimized' const contractOptimizedLLVMFilename = contractSolFilename + ".C.optimized";
const contractUnoptimizedLLVMFilename = contractSolFilename + '.C.unoptimized' const contractUnoptimizedLLVMFilename = contractSolFilename + ".C.unoptimized";
const pathToOutputDir = path.join(__dirname, '..', outputDir) const pathToOutputDir = path.join(__dirname, "..", outputDir);
const pathToContracts = path.join(__dirname, '..', 'src', 'contracts') const pathToContracts = path.join(__dirname, "..", "src", "contracts");
const pathToBasicYulContract = path.join( const pathToBasicYulContract = path.join(
pathToContracts, pathToContracts,
'yul', "yul",
contractYulFilename contractYulFilename,
) );
const pathToBasicSolContract = path.join( const pathToBasicSolContract = path.join(
pathToContracts, pathToContracts,
'solidity', "solidity",
contractSolFilename contractSolFilename,
) );
const pathToSolBinOutputFile = path.join( const pathToSolBinOutputFile = path.join(
pathToOutputDir, pathToOutputDir,
contractSolFilename + binExtension contractSolFilename + binExtension,
) );
const pathToSolAsmOutputFile = path.join( const pathToSolAsmOutputFile = path.join(
pathToOutputDir, pathToOutputDir,
contractSolFilename + asmExtension contractSolFilename + asmExtension,
) );
export const paths = { export const paths = {
outputDir: outputDir, outputDir: outputDir,
@@ -44,4 +44,4 @@ export const paths = {
pathToBasicYulContract: pathToBasicYulContract, pathToBasicYulContract: pathToBasicYulContract,
pathToSolBinOutputFile: pathToSolBinOutputFile, pathToSolBinOutputFile: pathToSolBinOutputFile,
pathToSolAsmOutputFile: pathToSolAsmOutputFile, pathToSolAsmOutputFile: pathToSolAsmOutputFile,
} };
@@ -1,51 +1,51 @@
import * as shell from 'shelljs' import * as shell from "shelljs";
import * as fs from 'fs' import * as fs from "fs";
import { spawnSync } from 'child_process' import { spawnSync } from "child_process";
interface CommandResult { interface CommandResult {
output: string output: string;
exitCode: number exitCode: number;
} }
export const executeCommand = ( export const executeCommand = (
command: string, command: string,
stdin?: string stdin?: string,
): CommandResult => { ): CommandResult => {
if (stdin) { if (stdin) {
const process = spawnSync(command, [], { const process = spawnSync(command, [], {
input: stdin, input: stdin,
shell: true, shell: true,
encoding: 'utf8', encoding: "utf8",
maxBuffer: 30 * 1024 * 1024, maxBuffer: 30 * 1024 * 1024,
}) });
return { return {
exitCode: process.status || 0, exitCode: process.status || 0,
output: (process.stdout || process.stderr || '').toString(), output: (process.stdout || process.stderr || "").toString(),
} };
} }
const result = shell.exec(command, { silent: true, async: false }) const result = shell.exec(command, { silent: true, async: false });
return { return {
exitCode: result.code, exitCode: result.code,
output: result.stdout || result.stderr || '', output: result.stdout || result.stderr || "",
} };
} };
export const isFolderExist = (folder: string): boolean => { export const isFolderExist = (folder: string): boolean => {
return shell.test('-d', folder) return shell.test("-d", folder);
} };
export const isFileExist = ( export const isFileExist = (
pathToFileDir: string, pathToFileDir: string,
fileName: string, fileName: string,
fileExtension: string fileExtension: string,
): boolean => { ): boolean => {
return shell.ls(pathToFileDir).stdout.includes(fileName + fileExtension) return shell.ls(pathToFileDir).stdout.includes(fileName + fileExtension);
} };
export const isFileEmpty = (file: string): boolean => { export const isFileEmpty = (file: string): boolean => {
if (fs.existsSync(file)) { if (fs.existsSync(file)) {
return fs.readFileSync(file).length === 0 return fs.readFileSync(file).length === 0;
} }
} };
@@ -1,44 +1,44 @@
import { executeCommand } from '../src/helper' import { executeCommand } from "../src/helper";
import { paths } from '../src/entities' import { paths } from "../src/entities";
//id1746 //id1746
describe('Run with --asm by default', () => { describe("Run with --asm by default", () => {
const command = `resolc ${paths.pathToBasicSolContract} --asm` const command = `resolc ${paths.pathToBasicSolContract} --asm`;
const result = executeCommand(command) const result = executeCommand(command);
const commandInvalid = 'resolc --asm' const commandInvalid = "resolc --asm";
const resultInvalid = executeCommand(commandInvalid) const resultInvalid = executeCommand(commandInvalid);
it('Valid command exit code = 0', () => { it("Valid command exit code = 0", () => {
expect(result.exitCode).toBe(0) expect(result.exitCode).toBe(0);
}) });
it('--asm output is presented', () => { it("--asm output is presented", () => {
const expectedPatterns = [/(deploy)/i, /(call)/i, /(seal_return)/i] const expectedPatterns = [/(deploy)/i, /(call)/i, /(seal_return)/i];
for (const pattern of expectedPatterns) { for (const pattern of expectedPatterns) {
expect(result.output).toMatch(pattern) expect(result.output).toMatch(pattern);
} }
}) });
it('solc exit code == resolc exit code', () => { it("solc exit code == resolc exit code", () => {
const command = `solc ${paths.pathToBasicSolContract} --asm` const command = `solc ${paths.pathToBasicSolContract} --asm`;
const solcResult = executeCommand(command) const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(result.exitCode) expect(solcResult.exitCode).toBe(result.exitCode);
}) });
it('run invalid: resolc --asm', () => { it("run invalid: resolc --asm", () => {
expect(resultInvalid.output).toMatch( expect(resultInvalid.output).toMatch(
/(No input sources specified|Compilation aborted)/i /(No input sources specified|Compilation aborted)/i,
) );
}) });
it('Invalid command exit code = 1', () => { it("Invalid command exit code = 1", () => {
expect(resultInvalid.exitCode).toBe(1) expect(resultInvalid.exitCode).toBe(1);
}) });
it('Invalid solc exit code == Invalid resolc exit code', () => { it("Invalid solc exit code == Invalid resolc exit code", () => {
const command = 'solc --asm' const command = "solc --asm";
const solcResult = executeCommand(command) const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(resultInvalid.exitCode) expect(solcResult.exitCode).toBe(resultInvalid.exitCode);
}) });
}) });
@@ -3,239 +3,239 @@ import {
isFolderExist, isFolderExist,
isFileExist, isFileExist,
isFileEmpty, isFileEmpty,
} from '../src/helper' } from "../src/helper";
import { paths } from '../src/entities' import { paths } from "../src/entities";
import * as shell from 'shelljs' import * as shell from "shelljs";
import * as path from 'path' import * as path from "path";
//id1762 //id1762
describe('Run resolc without any options', () => { describe("Run resolc without any options", () => {
const command = 'resolc' const command = "resolc";
const result = executeCommand(command) const result = executeCommand(command);
it('Info with help is presented', () => { it("Info with help is presented", () => {
expect(result.output).toMatch(/(Usage: resolc)/i) expect(result.output).toMatch(/(Usage: resolc)/i);
}) });
it('Exit code = 1', () => { it("Exit code = 1", () => {
expect(result.exitCode).toBe(1) expect(result.exitCode).toBe(1);
}) });
it('solc exit code == resolc exit code', () => { it("solc exit code == resolc exit code", () => {
const command = 'solc' const command = "solc";
const solcResult = executeCommand(command) const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(result.exitCode) expect(solcResult.exitCode).toBe(result.exitCode);
}) });
}) });
//#1713 //#1713
describe('Default run a command from the help', () => { describe("Default run a command from the help", () => {
const command = `resolc ${paths.pathToBasicSolContract} --overwrite -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", () => {
expect(result.output).toMatch(/(Compiler run successful.)/i) expect(result.output).toMatch(/(Compiler run successful.)/i);
}) });
it('Exit code = 0', () => { it("Exit code = 0", () => {
expect(result.exitCode).toBe(0) expect(result.exitCode).toBe(0);
}) });
it('Output dir is created', () => { it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true) expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
}) });
xit('Output file is created', () => { xit("Output file is created", () => {
// a bug on windows // a bug on windows
expect( expect(
isFileExist( isFileExist(
paths.pathToOutputDir, paths.pathToOutputDir,
paths.contractSolFilename, paths.contractSolFilename,
paths.binExtension paths.binExtension,
) ),
).toBe(true) ).toBe(true);
}) });
it('the output file is not empty', () => { it("the output file is not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false) expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
}) });
it("No 'Error'/'Warning'/'Fail' in the output", () => { it("No 'Error'/'Warning'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i) expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i);
}) });
}) });
//#1818 //#1818
describe('Default run a command from the help', () => { describe("Default run a command from the help", () => {
const command = `resolc ${paths.pathToBasicSolContract} --overwrite -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", () => {
expect(result.output).toMatch(/(Compiler run successful.)/i) expect(result.output).toMatch(/(Compiler run successful.)/i);
}) });
it('Exit code = 0', () => { it("Exit code = 0", () => {
expect(result.exitCode).toBe(0) expect(result.exitCode).toBe(0);
}) });
it('Output dir is created', () => { it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true) expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
}) });
xit('Output files are created', () => { xit("Output files are created", () => {
// a bug on windows // a bug on windows
expect( expect(
isFileExist( isFileExist(
paths.pathToOutputDir, paths.pathToOutputDir,
paths.contractSolFilename, paths.contractSolFilename,
paths.binExtension paths.binExtension,
) ),
).toBe(true) ).toBe(true);
expect( expect(
isFileExist( isFileExist(
paths.pathToOutputDir, paths.pathToOutputDir,
paths.contractSolFilename, paths.contractSolFilename,
paths.asmExtension paths.asmExtension,
) ),
).toBe(true) ).toBe(true);
}) });
it('the output files are not empty', () => { it("the output files are not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false) expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false) expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
}) });
it("No 'Error'/'Warning'/'Fail' in the output", () => { it("No 'Error'/'Warning'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i) expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i);
}) });
}) });
describe('Run resolc with source debug information', () => { describe("Run resolc with source debug information", () => {
const commands = [ const commands = [
`resolc -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`, `resolc -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`,
`resolc --disable-solc-optimizer -g ${paths.pathToBasicSolContract} --overwrite --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) {
const command = commands[idx] const command = commands[idx];
const result = executeCommand(command) const result = executeCommand(command);
it('Compiler run successful', () => { it("Compiler run successful", () => {
expect(result.output).toMatch(/(Compiler run successful.)/i) expect(result.output).toMatch(/(Compiler run successful.)/i);
}) });
it('Exit code = 0', () => { it("Exit code = 0", () => {
expect(result.exitCode).toBe(0) expect(result.exitCode).toBe(0);
}) });
it('Output dir is created', () => { it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true) expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
}) });
it('Output files are created', () => { it("Output files are created", () => {
// a bug on windows // a bug on windows
expect( expect(
isFileExist( isFileExist(
paths.pathToOutputDir, paths.pathToOutputDir,
paths.contractSolFilename, paths.contractSolFilename,
paths.binExtension paths.binExtension,
) ),
).toBe(true) ).toBe(true);
expect( expect(
isFileExist( isFileExist(
paths.pathToOutputDir, paths.pathToOutputDir,
paths.contractSolFilename, paths.contractSolFilename,
paths.asmExtension paths.asmExtension,
) ),
).toBe(true) ).toBe(true);
}) });
it('the output files are not empty', () => { it("the output files are not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false) expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false) expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
}) });
it("No 'Error'/'Fail' in the output", () => { it("No 'Error'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i) expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i);
}) });
} }
}) });
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} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`, `resolc -g ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`,
`resolc -g --disable-solc-optimizer ${paths.pathToBasicSolContract} --overwrite --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) {
const command = commands[idx] const command = commands[idx];
const result = executeCommand(command) const result = executeCommand(command);
it('Compiler run successful', () => { it("Compiler run successful", () => {
expect(result.output).toMatch(/(Compiler run successful.)/i) expect(result.output).toMatch(/(Compiler run successful.)/i);
}) });
it('Exit code = 0', () => { it("Exit code = 0", () => {
expect(result.exitCode).toBe(0) expect(result.exitCode).toBe(0);
}) });
it('Output dir is created', () => { it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true) expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
}) });
it('Output files are created', () => { it("Output files are created", () => {
// a bug on windows // a bug on windows
expect( expect(
isFileExist( isFileExist(
paths.pathToOutputDir, paths.pathToOutputDir,
paths.contractOptimizedLLVMFilename, paths.contractOptimizedLLVMFilename,
paths.llvmExtension paths.llvmExtension,
) ),
).toBe(true) ).toBe(true);
expect( expect(
isFileExist( isFileExist(
paths.pathToOutputDir, paths.pathToOutputDir,
paths.contractUnoptimizedLLVMFilename, paths.contractUnoptimizedLLVMFilename,
paths.llvmExtension paths.llvmExtension,
) ),
).toBe(true) ).toBe(true);
}) });
it('the output files are not empty', () => { it("the output files are not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false) expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false) expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
}) });
it("No 'Error'/'Fail' in the output", () => { it("No 'Error'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i) expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i);
}) });
} }
}) });
describe('Standard JSON compilation with path options', () => { describe("Standard JSON compilation with path options", () => {
const contractsDir = path.join(shell.tempdir(), 'contracts-test') const contractsDir = path.join(shell.tempdir(), "contracts-test");
const inputFile = path.join(__dirname, '..', 'src/contracts/compiled/1.json') const inputFile = path.join(__dirname, "..", "src/contracts/compiled/1.json");
beforeAll(() => { beforeAll(() => {
shell.mkdir('-p', contractsDir) shell.mkdir("-p", contractsDir);
const input = JSON.parse(shell.cat(inputFile).toString()) const input = JSON.parse(shell.cat(inputFile).toString());
Object.entries(input.sources).forEach( Object.entries(input.sources).forEach(
([sourcePath, source]: [string, any]) => { ([sourcePath, source]: [string, any]) => {
const filePath = path.join(contractsDir, sourcePath) const filePath = path.join(contractsDir, sourcePath);
shell.mkdir('-p', path.dirname(filePath)) shell.mkdir("-p", path.dirname(filePath));
shell.ShellString(source.content).to(filePath) shell.ShellString(source.content).to(filePath);
} },
) );
}) });
afterAll(() => { afterAll(() => {
shell.rm('-rf', contractsDir) shell.rm("-rf", contractsDir);
}) });
describe('Output with all path options', () => { describe("Output with all path options", () => {
let result: { exitCode: number; output: string } let result: { exitCode: number; output: string };
beforeAll(() => { beforeAll(() => {
const tempInputFile = path.join(contractsDir, 'temp-input.json') const tempInputFile = path.join(contractsDir, "temp-input.json");
shell.cp(inputFile, tempInputFile) shell.cp(inputFile, tempInputFile);
const inputContent = shell.cat(inputFile).toString() const inputContent = shell.cat(inputFile).toString();
const command = `resolc --standard-json --base-path "${contractsDir}" --include-path "${contractsDir}" --allow-paths "${contractsDir}"` const command = `resolc --standard-json --base-path "${contractsDir}" --include-path "${contractsDir}" --allow-paths "${contractsDir}"`;
result = executeCommand(command, inputContent) result = executeCommand(command, inputContent);
shell.rm(tempInputFile) shell.rm(tempInputFile);
}) });
it('Compiler run successful without emiting warnings', () => { it("Compiler run successful without emiting warnings", () => {
const parsedResults = JSON.parse(result.output) const parsedResults = JSON.parse(result.output);
expect( expect(
parsedResults.errors.filter( parsedResults.errors.filter(
(error: { type: string }) => error.type != 'Warning' (error: { type: string }) => error.type != "Warning",
) ),
).toEqual([]) ).toEqual([]);
}) });
}) });
}) });
@@ -1,39 +1,39 @@
import { executeCommand } from '../src/helper' import { executeCommand } from "../src/helper";
import { paths } from '../src/entities' import { paths } from "../src/entities";
//id1743 //id1743
describe('Run with --yul by default', () => { describe("Run with --yul by default", () => {
const command = `resolc ${paths.pathToBasicYulContract} --yul` const command = `resolc ${paths.pathToBasicYulContract} --yul`;
const result = executeCommand(command) const result = executeCommand(command);
const commandInvalid = 'resolc --yul' const commandInvalid = "resolc --yul";
const resultInvalid = executeCommand(commandInvalid) const resultInvalid = executeCommand(commandInvalid);
it('Valid command exit code = 0', () => { it("Valid command exit code = 0", () => {
expect(result.exitCode).toBe(0) expect(result.exitCode).toBe(0);
}) });
it('--yul output is presented', () => { it("--yul output is presented", () => {
expect(result.output).toMatch(/(Compiler run successful)/i) expect(result.output).toMatch(/(Compiler run successful)/i);
expect(result.output).toMatch(/(No output requested)/i) expect(result.output).toMatch(/(No output requested)/i);
}) });
xit('solc exit code == resolc exit code', () => { xit("solc exit code == resolc exit code", () => {
// unknown solc issue for datatype of the contract // unknown solc issue for datatype of the contract
const command = `solc ${paths.pathToBasicSolContract} --yul` const command = `solc ${paths.pathToBasicSolContract} --yul`;
const solcResult = executeCommand(command) const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(result.exitCode) expect(solcResult.exitCode).toBe(result.exitCode);
}) });
it('run invalid: resolc --yul', () => { it("run invalid: resolc --yul", () => {
expect(resultInvalid.output).toMatch(/(The input file is missing)/i) expect(resultInvalid.output).toMatch(/(The input file is missing)/i);
}) });
it('Invalid command exit code = 1', () => { it("Invalid command exit code = 1", () => {
expect(resultInvalid.exitCode).toBe(1) expect(resultInvalid.exitCode).toBe(1);
}) });
it('Invalid solc exit code == Invalid resolc exit code', () => { it("Invalid solc exit code == Invalid resolc exit code", () => {
const command = 'solc --yul' const command = "solc --yul";
const solcResult = executeCommand(command) const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(resultInvalid.exitCode) expect(solcResult.exitCode).toBe(resultInvalid.exitCode);
}) });
}) });
@@ -2,6 +2,6 @@
"compilerOptions": { "compilerOptions": {
"target": "ES6", "target": "ES6",
"module": "CommonJS", "module": "CommonJS",
"outDir": "./dist", "outDir": "./dist"
} }
} }
+104 -100
View File
@@ -1,9 +1,9 @@
import { executeCommand } from '../src/helper' import { executeCommand } from "../src/helper";
import { paths } from '../src/entities' import { paths } from "../src/entities";
describe('Set of --combined-json tests', () => { describe("Set of --combined-json tests", () => {
const zksolcCommand = 'zksolc' const zksolcCommand = "zksolc";
const solcCommand = 'solc' const solcCommand = "solc";
const json_args: string[] = [ const json_args: string[] = [
`abi`, `abi`,
`hashes`, `hashes`,
@@ -15,45 +15,49 @@ describe('Set of --combined-json tests', () => {
`asm`, `asm`,
`bin`, `bin`,
`bin-runtime`, `bin-runtime`,
] ];
//id1742:I //id1742:I
describe(`Run ${zksolcCommand} with just --combined-json`, () => { describe(`Run ${zksolcCommand} with just --combined-json`, () => {
const args = [`--combined-json`] const args = [`--combined-json`];
const result = executeCommand(zksolcCommand, args) const result = executeCommand(zksolcCommand, args);
it('Valid command exit code = 1', () => { it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1) expect(result.exitCode).toBe(1);
}) });
it('--combined-json error is presented', () => { it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(requires a value but none was supplied)/i) expect(result.output).toMatch(
}) /(requires a value but none was supplied)/i,
);
});
it('solc exit code == zksolc exit code', () => { it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args) const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode) expect(solcResult.exitCode).toBe(result.exitCode);
}) });
}) });
//id1742:II //id1742:II
describe(`Run ${zksolcCommand} with Sol contract and --combined-json`, () => { describe(`Run ${zksolcCommand} with Sol contract and --combined-json`, () => {
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`] const args = [`${paths.pathToBasicSolContract}`, `--combined-json`];
const result = executeCommand(zksolcCommand, args) const result = executeCommand(zksolcCommand, args);
it('Valid command exit code = 1', () => { it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1) expect(result.exitCode).toBe(1);
}) });
it('--combined-json error is presented', () => { it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(requires a value but none was supplied)/i) expect(result.output).toMatch(
}) /(requires a value but none was supplied)/i,
);
});
it('solc exit code == zksolc exit code', () => { it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args) const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode) expect(solcResult.exitCode).toBe(result.exitCode);
}) });
}) });
//id1742:III //id1742:III
for (let i = 0; i < json_args.length; i++) { for (let i = 0; i < json_args.length; i++) {
@@ -62,22 +66,22 @@ describe('Set of --combined-json tests', () => {
`${paths.pathToBasicSolContract}`, `${paths.pathToBasicSolContract}`,
`--combined-json`, `--combined-json`,
`${json_args[i]}`, `${json_args[i]}`,
] ];
const result = executeCommand(zksolcCommand, args) const result = executeCommand(zksolcCommand, args);
it('Valid command exit code = 0', () => { it("Valid command exit code = 0", () => {
expect(result.exitCode).toBe(0) expect(result.exitCode).toBe(0);
}) });
it('--combined-json error is presented', () => { it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(contracts)/i) expect(result.output).toMatch(/(contracts)/i);
}) });
it('solc exit code == zksolc exit code', () => { it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args) const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode) expect(solcResult.exitCode).toBe(result.exitCode);
}) });
}) });
} }
//id1829:I //id1829:I
@@ -87,22 +91,22 @@ describe('Set of --combined-json tests', () => {
`${paths.pathToBasicSolContract}`, `${paths.pathToBasicSolContract}`,
`--combined-json`, `--combined-json`,
`--${json_args[i]}`, `--${json_args[i]}`,
] ];
const result = executeCommand(zksolcCommand, args) const result = executeCommand(zksolcCommand, args);
it('Valid command exit code = 1', () => { it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1) expect(result.exitCode).toBe(1);
}) });
it('--combined-json error is presented', () => { it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(Invalid option|error)/i) expect(result.output).toMatch(/(Invalid option|error)/i);
}) });
it('solc exit code == zksolc exit code', () => { it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args) const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode) expect(solcResult.exitCode).toBe(result.exitCode);
}) });
}) });
} }
//id1829:II //id1829:II
@@ -113,24 +117,24 @@ describe('Set of --combined-json tests', () => {
`--combined-json`, `--combined-json`,
`${json_args[i]}`, `${json_args[i]}`,
`${json_args[i]}`, `${json_args[i]}`,
] ];
const result = executeCommand(zksolcCommand, args) const result = executeCommand(zksolcCommand, args);
xit('Valid command exit code = 1', () => { xit("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1) expect(result.exitCode).toBe(1);
}) });
it('--combined-json error is presented', () => { it("--combined-json error is presented", () => {
expect(result.output).toMatch( expect(result.output).toMatch(
/(No such file or directory|cannot find the file specified)/i /(No such file or directory|cannot find the file specified)/i,
) // Hopefully we should have more precise message here! ); // Hopefully we should have more precise message here!
}) });
xit('solc exit code == zksolc exit code', () => { xit("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args) const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode) expect(solcResult.exitCode).toBe(result.exitCode);
}) });
}) });
} }
//id1829:III //id1829:III
@@ -142,22 +146,22 @@ describe('Set of --combined-json tests', () => {
`${json_args[i]}`, `${json_args[i]}`,
`--combined-json`, `--combined-json`,
`${json_args[i]}`, `${json_args[i]}`,
] ];
const result = executeCommand(zksolcCommand, args) const result = executeCommand(zksolcCommand, args);
it('Valid command exit code = 1', () => { it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1) expect(result.exitCode).toBe(1);
}) });
it('--combined-json error is presented', () => { it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(cannot be used multiple times)/i) expect(result.output).toMatch(/(cannot be used multiple times)/i);
}) });
it('solc exit code == zksolc exit code', () => { it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args) const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode) expect(solcResult.exitCode).toBe(result.exitCode);
}) });
}) });
} }
//id1830 //id1830
@@ -167,22 +171,22 @@ describe('Set of --combined-json tests', () => {
`${paths.pathToBasicYulContract}`, `${paths.pathToBasicYulContract}`,
`--combined-json`, `--combined-json`,
`${json_args[i]}`, `${json_args[i]}`,
] ];
const result = executeCommand(zksolcCommand, args) const result = executeCommand(zksolcCommand, args);
it('Valid command exit code = 1', () => { it("Valid command exit code = 1", () => {
expect(result.exitCode).toBe(1) expect(result.exitCode).toBe(1);
}) });
it('--combined-json error is presented', () => { it("--combined-json error is presented", () => {
expect(result.output).toMatch(/(ParserError: Expected identifier)/i) expect(result.output).toMatch(/(ParserError: Expected identifier)/i);
}) });
asd asd;
it('solc exit code == zksolc exit code', () => { it("solc exit code == zksolc exit code", () => {
const solcResult = executeCommand(solcCommand, args) const solcResult = executeCommand(solcCommand, args);
expect(solcResult.exitCode).toBe(result.exitCode) expect(solcResult.exitCode).toBe(result.exitCode);
}) });
}) });
} }
}) });
-1
View File
@@ -1 +0,0 @@
../../../../target/wasm32-unknown-emscripten/release/resolc.wasm
-1
View File
@@ -1 +0,0 @@
../../../../target/wasm32-unknown-emscripten/release/resolc_web.js
-138
View File
@@ -1,138 +0,0 @@
import { expect } from 'chai'
import { compile } from '../examples/node/revive.js'
import { fileURLToPath } from 'url'
import path from 'path'
import fs from 'fs'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
function loadFixture(fixture) {
const fixturePath = path.resolve(__dirname, `../fixtures/${fixture}`)
return JSON.parse(fs.readFileSync(fixturePath, 'utf-8'))
}
describe('Compile Function Tests', function () {
it('should successfully compile valid Solidity code', async function () {
const standardInput = loadFixture('storage.json')
const result = await compile(standardInput)
expect(result).to.be.a('string')
const output = JSON.parse(result)
expect(output).to.have.property('contracts')
expect(output.contracts['fixtures/storage.sol']).to.have.property('Storage')
expect(output.contracts['fixtures/storage.sol'].Storage).to.have.property(
'abi'
)
expect(output.contracts['fixtures/storage.sol'].Storage).to.have.property(
'evm'
)
expect(
output.contracts['fixtures/storage.sol'].Storage.evm
).to.have.property('bytecode')
})
if (typeof globalThis.Bun == 'undefined') {
// Running this test with Bun on a Linux host causes:
// RuntimeError: Out of bounds memory access (evaluating 'getWasmTableEntry(index)(a1, a2, a3, a4, a5)')
// Once this issue is resolved, the test will be re-enabled.
it('should successfully compile large Solidity code', async function () {
const standardInput = loadFixture('token.json')
const result = await compile(standardInput)
expect(result).to.be.a('string')
const output = JSON.parse(result)
expect(output).to.have.property('contracts')
expect(output.contracts['fixtures/token.sol']).to.have.property('MyToken')
expect(output.contracts['fixtures/token.sol'].MyToken).to.have.property(
'abi'
)
expect(output.contracts['fixtures/token.sol'].MyToken).to.have.property(
'evm'
)
expect(
output.contracts['fixtures/token.sol'].MyToken.evm
).to.have.property('bytecode')
})
it('should successfully compile a valid Solidity contract that instantiates the token contracts', async function () {
const standardInput = loadFixture('instantiate_tokens.json')
const result = await compile(standardInput)
expect(result).to.be.a('string')
const output = JSON.parse(result)
expect(output).to.have.property('contracts')
expect(
output.contracts['fixtures/instantiate_tokens.sol']
).to.have.property('TokensFactory')
expect(
output.contracts['fixtures/instantiate_tokens.sol'].TokensFactory
).to.have.property('abi')
expect(
output.contracts['fixtures/instantiate_tokens.sol'].TokensFactory
).to.have.property('evm')
expect(
output.contracts['fixtures/instantiate_tokens.sol'].TokensFactory.evm
).to.have.property('bytecode')
})
}
it('should throw an error for invalid Solidity code', async function () {
const standardInput = loadFixture('invalid_contract_content.json')
const result = await compile(standardInput)
expect(result).to.be.a('string')
const output = JSON.parse(result)
expect(output).to.have.property('errors')
expect(output.errors).to.be.an('array')
expect(output.errors.length).to.be.greaterThan(0)
expect(output.errors[0].type).to.exist
expect(output.errors[0].type).to.contain('ParserError')
})
it('should return not found error for missing imports', async function () {
const standardInput = loadFixture('missing_import.json')
const result = await compile(standardInput)
const output = JSON.parse(result)
expect(output).to.have.property('errors')
expect(output.errors).to.be.an('array')
expect(output.errors.length).to.be.greaterThan(0)
expect(output.errors[0].message).to.exist
expect(output.errors[0].message).to.include(
'Source "nonexistent/console.sol" not found'
)
})
it('should successfully compile a valid Solidity contract that instantiates another contract', async function () {
const standardInput = loadFixture('instantiate.json')
const result = await compile(standardInput)
expect(result).to.be.a('string')
const output = JSON.parse(result)
expect(output).to.have.property('contracts')
expect(output.contracts['fixtures/instantiate.sol']).to.have.property(
'ChildContract'
)
expect(
output.contracts['fixtures/instantiate.sol'].ChildContract
).to.have.property('abi')
expect(
output.contracts['fixtures/instantiate.sol'].ChildContract
).to.have.property('evm')
expect(
output.contracts['fixtures/instantiate.sol'].ChildContract.evm
).to.have.property('bytecode')
expect(output.contracts['fixtures/instantiate.sol']).to.have.property(
'MainContract'
)
expect(
output.contracts['fixtures/instantiate.sol'].MainContract
).to.have.property('abi')
expect(
output.contracts['fixtures/instantiate.sol'].MainContract
).to.have.property('evm')
expect(
output.contracts['fixtures/instantiate.sol'].MainContract.evm
).to.have.property('bytecode')
})
})
+6
View File
@@ -0,0 +1,6 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": false,
"singleQuote": true
}
+5 -2
View File
@@ -1,7 +1,10 @@
# @parity/revive
Node.js module to compile Solidity contracts to Polkavm bytecode, using [Revive](https://github.com/paritytech/revive)
# Usage from Node.js # Usage from Node.js
```typescript ```typescript
import { compile } from "@parity/resolc";
const sources = { const sources = {
["contracts/1_Storage.sol"]: { ["contracts/1_Storage.sol"]: {
content: readFileSync("fixtures/storage.sol", "utf8"), content: readFileSync("fixtures/storage.sol", "utf8"),
@@ -13,5 +16,5 @@ const out = await compile(sources);
# Usage from shell # Usage from shell
```bash ```bash
npx @parity/resolc@latest --bin contracts/1_Storage.sol npx @parity/revive@latest --bin contracts/1_Storage.sol
``` ```
+7 -3
View File
@@ -24,14 +24,18 @@
"test": "npm run build && node ./dist/index.test.js" "test": "npm run build && node ./dist/index.test.js"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.14.0",
"@openzeppelin/contracts": "5.1.0", "@openzeppelin/contracts": "5.1.0",
"@types/node": "^22.14.1",
"eslint": "^9.14.0",
"globals": "^15.12.0", "globals": "^15.12.0",
"typescript": "^5.6.3" "typescript": "^5.6.3",
"typescript-eslint": "^8.13.0"
}, },
"dependencies": { "dependencies": {
"@types/node": "^22.9.0",
"commander": "^13.1.0", "commander": "^13.1.0",
"package-json": "^10.0.1", "package-json": "^10.0.1",
"solc": "^0.8.29" "solc": "^0.8.29"
} },
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
} }
+5 -2
View File
@@ -169,7 +169,8 @@ async function main() {
writeFile( writeFile(
contractFileName + '.polkavm', contractFileName + '.polkavm',
Buffer.from( Buffer.from(
output.contracts[fileName][contractName].evm.bytecode.object, output.contracts[fileName][contractName].evm.bytecode
.object,
'hex' 'hex'
) )
) )
@@ -178,7 +179,9 @@ async function main() {
if (options.abi) { if (options.abi) {
writeFile( writeFile(
contractFileName + '.abi', contractFileName + '.abi',
toFormattedJson(output.contracts[fileName][contractName].abi) toFormattedJson(
output.contracts[fileName][contractName].abi
)
) )
} }
} }
+28 -12
View File
@@ -1,12 +1,22 @@
import { test } from 'node:test' import { test } from 'node:test'
import { readFileSync, existsSync } from 'node:fs' import { readFileSync } from 'node:fs'
import assert from 'node:assert' import assert from 'node:assert'
import { compile, tryResolveImport } from '.' import { compile, tryResolveImport } from '.'
import { resolve } from 'node:path' import { resolve } from 'node:path'
import { execSync } from 'node:child_process'
function isResolcInPath() {
try {
execSync('resolc --version', { stdio: 'ignore' })
return true
} catch {
return false
}
}
const compileOptions = [{}] const compileOptions = [{}]
if (existsSync('../../target/release/resolc')) { if (isResolcInPath()) {
compileOptions.push({ bin: '../../target/release/resolc' }) compileOptions.push({ bin: 'resolc' })
} }
for (const options of compileOptions) { for (const options of compileOptions) {
@@ -56,7 +66,9 @@ test('check Err from stderr', async () => {
assert(false, 'Expected error') assert(false, 'Expected error')
} catch (error) { } catch (error) {
assert( assert(
String(error).includes('Source file requires different compiler version') String(error).includes(
'Source file requires different compiler version'
)
) )
} }
}) })
@@ -71,15 +83,15 @@ test('resolve import', () => {
// scopped module with version // scopped module with version
{ {
file: '@openzeppelin/contracts@5.1.0/token/ERC20/ERC20.sol', file: '@openzeppelin/contracts@5.1.0/token/ERC20/ERC20.sol',
expected: require.resolve( expected: resolve(
'@openzeppelin/contracts/token/ERC20/ERC20.sol' 'node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol'
), ),
}, },
// scopped module without version // scopped module without version
{ {
file: '@openzeppelin/contracts/token/ERC20/ERC20.sol', file: '@openzeppelin/contracts/token/ERC20/ERC20.sol',
expected: require.resolve( expected: resolve(
'@openzeppelin/contracts/token/ERC20/ERC20.sol' 'node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol'
), ),
}, },
// scopped module with wrong version // scopped module with wrong version
@@ -89,13 +101,17 @@ test('resolve import', () => {
}, },
// module without version // module without version
{ {
file: '@openzeppelin/contracts/package.json', file: 'acorn/package.json',
expected: require.resolve('@openzeppelin/contracts/package.json'), expected: resolve('node_modules/acorn/package.json'),
}, },
// scopped module with version // scopped module with version
{ {
file: '@openzeppelin/contracts@5.1.0/package.json', file: 'acorn@8.14.0/package.json',
expected: require.resolve('@openzeppelin/contracts/package.json'), expected: resolve('node_modules/acorn/package.json'),
},
{
file: 'acorn@8.14.1/package.json',
expected: `Error: Version mismatch: Specified acorn@8.14.1, but installed version is 8.14.0`,
}, },
] ]
+7 -18
View File
@@ -93,26 +93,13 @@ export function version(): string {
export async function compile( export async function compile(
sources: SolcInput, sources: SolcInput,
option: { option: { bin?: string } = {}
optimizer?: Record<string, unknown>
bin?: string
} = {}
): Promise<SolcOutput> { ): Promise<SolcOutput> {
const {
optimizer = {
mode: 'z',
fallback_to_optimizing_for_size: true,
enabled: true,
runs: 200,
},
bin,
} = option
const input = JSON.stringify({ const input = JSON.stringify({
language: 'Solidity', language: 'Solidity',
sources: resolveInputs(sources), sources: resolveInputs(sources),
settings: { settings: {
optimizer, optimizer: { enabled: true, runs: 200 },
outputSelection: { outputSelection: {
'*': { '*': {
'*': ['abi'], '*': ['abi'],
@@ -121,8 +108,8 @@ export async function compile(
}, },
}) })
if (bin) { if (option.bin) {
return compileWithBin(input, bin) return compileWithBin(input, option.bin)
} }
return resolc(input) return resolc(input)
@@ -151,7 +138,9 @@ export function tryResolveImport(importPath: string) {
let packageJsonPath let packageJsonPath
try { try {
packageJsonPath = require.resolve(path.join(basePackage, 'package.json')) packageJsonPath = require.resolve(
path.join(basePackage, 'package.json')
)
} catch { } catch {
throw new Error(`Could not resolve package ${basePackage}`) throw new Error(`Could not resolve package ${basePackage}`)
} }
+1 -1
View File
@@ -17,7 +17,7 @@
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"useUnknownInCatchVariables": true, "useUnknownInCatchVariables": true,
"types": ["node"], "types": ["node"],
"typeRoots": ["../../node_modules/@types", "./src"], "typeRoots": ["./node_modules/@types", "./src"],
"paths": { "paths": {
"#src/*": ["./src/*"] "#src/*": ["./src/*"]
}, },
+1
View File
@@ -0,0 +1 @@
../../../target/wasm32-unknown-emscripten/release/resolc.wasm
+1
View File
@@ -0,0 +1 @@
../../../target/wasm32-unknown-emscripten/release/resolc_web.js
+142
View File
@@ -0,0 +1,142 @@
import { expect } from "chai";
import { compile } from "../examples/node/revive.js";
import { fileURLToPath } from "url";
import path from "path";
import fs from "fs";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
function loadFixture(fixture) {
const fixturePath = path.resolve(__dirname, `../fixtures/${fixture}`);
return JSON.parse(fs.readFileSync(fixturePath, "utf-8"));
}
describe("Compile Function Tests", function () {
it("should successfully compile valid Solidity code", async function () {
const standardInput = loadFixture("storage.json");
const result = await compile(standardInput);
expect(result).to.be.a("string");
const output = JSON.parse(result);
expect(output).to.have.property("contracts");
expect(output.contracts["fixtures/storage.sol"]).to.have.property(
"Storage",
);
expect(output.contracts["fixtures/storage.sol"].Storage).to.have.property(
"abi",
);
expect(output.contracts["fixtures/storage.sol"].Storage).to.have.property(
"evm",
);
expect(
output.contracts["fixtures/storage.sol"].Storage.evm,
).to.have.property("bytecode");
});
if (typeof globalThis.Bun == "undefined") {
// Running this test with Bun on a Linux host causes:
// RuntimeError: Out of bounds memory access (evaluating 'getWasmTableEntry(index)(a1, a2, a3, a4, a5)')
// Once this issue is resolved, the test will be re-enabled.
it("should successfully compile large Solidity code", async function () {
const standardInput = loadFixture("token.json");
const result = await compile(standardInput);
expect(result).to.be.a("string");
const output = JSON.parse(result);
expect(output).to.have.property("contracts");
expect(output.contracts["fixtures/token.sol"]).to.have.property(
"MyToken",
);
expect(output.contracts["fixtures/token.sol"].MyToken).to.have.property(
"abi",
);
expect(output.contracts["fixtures/token.sol"].MyToken).to.have.property(
"evm",
);
expect(
output.contracts["fixtures/token.sol"].MyToken.evm,
).to.have.property("bytecode");
});
it("should successfully compile a valid Solidity contract that instantiates the token contracts", async function () {
const standardInput = loadFixture("instantiate_tokens.json");
const result = await compile(standardInput);
expect(result).to.be.a("string");
const output = JSON.parse(result);
expect(output).to.have.property("contracts");
expect(
output.contracts["fixtures/instantiate_tokens.sol"],
).to.have.property("TokensFactory");
expect(
output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory,
).to.have.property("abi");
expect(
output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory,
).to.have.property("evm");
expect(
output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory.evm,
).to.have.property("bytecode");
});
}
it("should throw an error for invalid Solidity code", async function () {
const standardInput = loadFixture("invalid_contract_content.json");
const result = await compile(standardInput);
expect(result).to.be.a("string");
const output = JSON.parse(result);
expect(output).to.have.property("errors");
expect(output.errors).to.be.an("array");
expect(output.errors.length).to.be.greaterThan(0);
expect(output.errors[0].type).to.exist;
expect(output.errors[0].type).to.contain("ParserError");
});
it("should return not found error for missing imports", async function () {
const standardInput = loadFixture("missing_import.json");
const result = await compile(standardInput);
const output = JSON.parse(result);
expect(output).to.have.property("errors");
expect(output.errors).to.be.an("array");
expect(output.errors.length).to.be.greaterThan(0);
expect(output.errors[0].message).to.exist;
expect(output.errors[0].message).to.include(
'Source "nonexistent/console.sol" not found',
);
});
it("should successfully compile a valid Solidity contract that instantiates another contract", async function () {
const standardInput = loadFixture("instantiate.json");
const result = await compile(standardInput);
expect(result).to.be.a("string");
const output = JSON.parse(result);
expect(output).to.have.property("contracts");
expect(output.contracts["fixtures/instantiate.sol"]).to.have.property(
"ChildContract",
);
expect(
output.contracts["fixtures/instantiate.sol"].ChildContract,
).to.have.property("abi");
expect(
output.contracts["fixtures/instantiate.sol"].ChildContract,
).to.have.property("evm");
expect(
output.contracts["fixtures/instantiate.sol"].ChildContract.evm,
).to.have.property("bytecode");
expect(output.contracts["fixtures/instantiate.sol"]).to.have.property(
"MainContract",
);
expect(
output.contracts["fixtures/instantiate.sol"].MainContract,
).to.have.property("abi");
expect(
output.contracts["fixtures/instantiate.sol"].MainContract,
).to.have.property("evm");
expect(
output.contracts["fixtures/instantiate.sol"].MainContract.evm,
).to.have.property("bytecode");
});
});
-6618
View File
File diff suppressed because it is too large Load Diff
+6 -11
View File
@@ -3,19 +3,14 @@
"private": true, "private": true,
"scripts": { "scripts": {
"test:cli": "npm run test -w crates/solidity/src/tests/cli-tests", "test:cli": "npm run test -w crates/solidity/src/tests/cli-tests",
"test:wasm": "npm run test:node -w js/emscripten", "test:wasm": "npm run test:node -w js/wasm",
"build:package": "npm run build:package -w js/emscripten", "build:package": "npm run build:package -w js/wasm",
"lint": "npx eslint 'js/**/*.{cjs,mjs,ts}' && npx prettier --check '**/*.{mjs,cjs,ts}'", "lint": "npx eslint 'js/**/*.{cjs,mjs,ts}' && npx prettier --check '**/*.{ts,json,yml,md}'",
"lint:fix": "npx prettier --write '**/*.{mjs,cjs,ts}'" "lint:fix": "npx prettier --write '**/*.{mjs,cjs,ts,json,yml,md}'"
}, },
"workspaces": [ "workspaces": [
"crates/solidity/src/tests/cli-tests", "crates/solidity/src/tests/cli-tests",
"js/emscripten", "js/wasm",
"js/resolc" "js/resolc"
], ]
"dependencies": {
"@eslint/js": "^9.14.0",
"eslint": "^9.14.0",
"typescript-eslint": "^8.13.0"
}
} }
+3635
View File
File diff suppressed because it is too large Load Diff