Compare commits

...

16 Commits

Author SHA1 Message Date
Cyrill Leutwiler 0c155d3f57 fix the LLVM builder with new toolchains
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-27 08:06:08 +02:00
xermicus 3389865af7 solc-json-interface: make the input Cloneable (#323)
It helps external consumers working with the
`revive-solc-json-interface` crate.

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-21 10:03:32 +02:00
xermicus af39d506d9 update emsdk (#324)
Update Emscripten SDK to latest version `v4.0.9`.

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-21 07:01:41 +02:00
xermicus bb2f829361 expose custom PVM settings in the standard json interface (#318)
Exposes the following PolkaVM specific options via the standard json
interface:
- Heap size
- Stack size
- Whether to emit source level debug information

Additionally it is now forbidden to specify those as CLI option in
standard JSON mode. They are bytecode altering options and having
multiple ways to specify them creates unnecessary room for confusion:
The standard JSON input description should be sufficient and succint for
reproducible builds.

Closes #290

---------

Signed-off-by: xermicus <bigcyrill@hotmail.com>
2025-05-13 15:19:00 +02:00
xermicus 1b8fcc4649 Update the Rust version (#316)
- Update the Rust version to 1.85
- Update the package-lock.json
- Update the musl-cross docker image

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-09 20:00:58 +02:00
Cyrill Leutwiler 0e9e405f21 remove any git dependencies
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-09 17:26:20 +02:00
xermicus 722dd86c27 remove STATUS.md (#315)
This information is provided in the docs.

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-09 12:21:25 +02:00
PG Herveou 0421869e4b Replace release 0.1.0-dev.15 with 0.1.0-dev.16 (#314)
add missing patch and rename release
2025-05-08 15:26:10 +02:00
xermicus 32f55b976c update changelog (#313)
somehow went wrong

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-08 14:16:46 +02:00
PG Herveou 459a786299 v0.1.0-dev.15 release (#311)
Co-authored-by: xermicus <cyrill@parity.io>
2025-05-08 14:09:55 +02:00
xermicus fbaa45f283 support solc v0.8.30 (#308)
- Add support for solc `v0.8.30`. No changes in YUL or the binary
interface.
- Fix a semver bug in the NPM packages to correctly align their solc
dependencies with our last supported version.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-08 13:29:13 +02:00
xermicus f2fac85dae add npm package information to release checklist (#312)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-08 13:14:28 +02:00
PG Herveou b8f3073e29 Run lint:fix (#309) 2025-05-08 12:36:11 +02:00
xermicus 11d47d74ac apply size optimizations by default (#298)
So far if no optimization level was specified, optimizations for
execution time were applied. However, we currently are a bit limited on
code size. Add to that, this setting is not available in solc and people
generally ignore the docs, generating a lot of support requests.

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-07 15:35:48 +02:00
xermicus e3a9c95d32 llvm-builder: use the ninja generator for building the builtins on windows (#299)
The builtins build should use the Ninja generator (MSVC does not build a
valid archive).

Tested and verified here:
https://github.com/paritytech/revive-alex-workflowtest/releases/tag/untagged-f02d0f574bab8404fead

Closes #305

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2025-05-07 11:10:58 +02:00
PG Herveou a560b2d919 Fix npm-release job (#297)
follow up from #295
2025-04-30 21:48:11 +02:00
59 changed files with 9816 additions and 3245 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ inputs:
version: version:
description: "" description: ""
required: false required: false
default: "3.1.64" default: "4.0.9"
runs: runs:
+1 -1
View File
@@ -19,7 +19,7 @@ runs:
shell: bash shell: bash
run: | run: |
mkdir -p solc mkdir -p solc
curl -sSL --output solc/solc https://github.com/ethereum/solidity/releases/download/v0.8.29/${SOLC_NAME} curl -sSL --output solc/solc https://github.com/ethereum/solidity/releases/download/v0.8.30/${SOLC_NAME}
- name: Make Solc Executable - name: Make Solc Executable
if: ${{ runner.os == 'Windows' }} if: ${{ runner.os == 'Windows' }}
+5
View File
@@ -87,6 +87,11 @@ 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
+21 -8
View File
@@ -14,7 +14,7 @@ concurrency:
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
RUST_MUSL_CROSS_IMAGE: messense/rust-musl-cross@sha256:68b86bc7cb2867259e6b233415a665ff4469c28b57763e78c3bfea1c68091561 RUST_MUSL_CROSS_IMAGE: messense/rust-musl-cross@sha256:c0154e992adb791c3b848dd008939d19862549204f8cb26f5ca7a00f629e6067
jobs: jobs:
check-version-changed: check-version-changed:
@@ -181,7 +181,7 @@ jobs:
- name: Basic Sanity Check - name: Basic Sanity Check
run: | run: |
mkdir -p solc mkdir -p solc
curl -sSLo solc/soljson.js https://github.com/ethereum/solidity/releases/download/v0.8.29/soljson.js curl -sSLo solc/soljson.js https://github.com/ethereum/solidity/releases/download/v0.8.30/soljson.js
node -e " node -e "
const soljson = require('solc/soljson'); const soljson = require('solc/soljson');
const createRevive = require('./target/wasm32-unknown-emscripten/release/resolc.js'); const createRevive = require('./target/wasm32-unknown-emscripten/release/resolc.js');
@@ -292,24 +292,37 @@ jobs:
runs-on: macos-14 runs-on: macos-14
environment: tags environment: tags
steps: steps:
- name: Build - uses: actions/checkout@v4
run: npm -w js/resolc run build
- name: Set version - name: Download Artifacts
run: npm -w js/resolc version --no-git-tag-version ${{github.event.release.tag_name}} 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: npm pack - name: npm pack
run: npm -w js/resolc pack run: npm -w js/resolc pack
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: package name: npm_package
path: "parity-resolc-*.tgz" path: "parity-resolc-*.tgz"
- uses: octokit/request-action@bbedc70b1981e610d89f1f8de88311a1fc02fb83 - uses: octokit/request-action@bbedc70b1981e610d89f1f8de88311a1fc02fb83
with: with:
route: POST /repos/paritytech/npm_publish_automation/actions/workflows/publish.yml/dispatches route: POST /repos/paritytech/npm_publish_automation/actions/workflows/publish.yml/dispatches
ref: main ref: main
inputs: '${{ format(''{{ "repo": "{0}", "run_id": "{1}" }}'', github.repository, github.run_id) }}' inputs: '${{ format(''{{ "artifact_name": "npm_package", "repo": "{0}", "run_id": "{1}" }}'', github.repository, github.run_id) }}'
env: env:
GITHUB_TOKEN: ${{ secrets.NPM_PUBLISH_AUTOMATION_TOKEN }} GITHUB_TOKEN: ${{ secrets.NPM_PUBLISH_AUTOMATION_TOKEN }}
-1
View File
@@ -12,7 +12,6 @@ target-llvm
node_modules node_modules
artifacts artifacts
tmp tmp
package-lock.json
/*.html /*.html
/build /build
soljson.js soljson.js
+1 -1
View File
@@ -2,5 +2,5 @@
"trailingComma": "es5", "trailingComma": "es5",
"tabWidth": 2, "tabWidth": 2,
"semi": false, "semi": false,
"singleQuote": false "singleQuote": true
} }
+61 -3
View File
@@ -4,14 +4,46 @@
This is a development pre-release. This is a development pre-release.
Supported `polkadot-sdk` rev: `2503.0.1`
## v0.1.0
This is a development pre-release.
Supported `polkadot-sdk` rev: `2503.0.1`
### Added
- Add the PolkaVM heap size, stack size and debug info CLI compiler options to the standard JSON settings. This makes the standard JSON input succint for reproducible builds.
### Changed
- Supported `polkadot-sdk` version is now `2503.0.1`
- The `emsdk` version is now `4.0.9`
### Fixed
- The LLVM builder with newer toolchains.
## v0.1.0-dev.16
This is a development pre-release.
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee` Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
### Added ### Added
- Move the npm package from paritytech/js-revive, into this repo. The package `@parity/resolc` will be deployed to npm for each release.
- Support for solc v0.8.30
### Changed ### Changed
- By default, heavy size optimizations are applied.
### Fixed ### Fixed
- @parity/resolc: The solc dependency package is constrained to the latest supported version, preventing breaking the package ever time a new solc package was released.
- The resolc npm package no longer ignores the optimizer settings
## v0.1.0-dev.14 ## v0.1.0-dev.14
This is a development pre-release. This is a development pre-release.
@@ -19,13 +51,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
@@ -35,16 +70,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
@@ -54,10 +92,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
@@ -73,6 +113,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.
@@ -83,10 +124,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.
@@ -96,6 +139,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.
@@ -109,9 +153,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
@@ -119,15 +165,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.
@@ -137,17 +186,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.
@@ -157,6 +209,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`.
@@ -164,21 +217,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
@@ -188,9 +244,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.
Generated
+1813 -2091
View File
File diff suppressed because it is too large Load Diff
+20 -21
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/*"] members = ["crates/*"]
[workspace.package] [workspace.package]
version = "0.1.0-dev.14" version = "0.1.0-dev.16"
authors = [ authors = [
"Cyrill Leutwiler <cyrill@parity.io>", "Cyrill Leutwiler <cyrill@parity.io>",
"Parity Technologies <admin@parity.io>", "Parity Technologies <admin@parity.io>",
@@ -11,27 +11,27 @@ authors = [
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
edition = "2021" edition = "2021"
repository = "https://github.com/paritytech/revive" repository = "https://github.com/paritytech/revive"
rust-version = "1.81.0" rust-version = "1.85.0"
[workspace.dependencies] [workspace.dependencies]
revive-benchmarks = { version = "0.1.0-dev.14", path = "crates/benchmarks" } revive-benchmarks = { version = "0.1.0-dev.16", path = "crates/benchmarks" }
revive-builtins = { version = "0.1.0-dev.14", path = "crates/builtins" } revive-builtins = { version = "0.1.0-dev.16", path = "crates/builtins" }
revive-common = { version = "0.1.0-dev.14", path = "crates/common" } revive-common = { version = "0.1.0-dev.16", path = "crates/common" }
revive-differential = { version = "0.1.0-dev.14", path = "crates/differential" } revive-differential = { version = "0.1.0-dev.16", path = "crates/differential" }
revive-integration = { version = "0.1.0-dev.14", path = "crates/integration" } revive-integration = { version = "0.1.0-dev.16", path = "crates/integration" }
revive-linker = { version = "0.1.0-dev.14", path = "crates/linker" } revive-linker = { version = "0.1.0-dev.16", path = "crates/linker" }
lld-sys = { version = "0.1.0-dev.14", path = "crates/lld-sys" } lld-sys = { version = "0.1.0-dev.16", path = "crates/lld-sys" }
revive-llvm-context = { version = "0.1.0-dev.14", path = "crates/llvm-context" } revive-llvm-context = { version = "0.1.0-dev.16", path = "crates/llvm-context" }
revive-runtime-api = { version = "0.1.0-dev.14", path = "crates/runtime-api" } revive-runtime-api = { version = "0.1.0-dev.16", path = "crates/runtime-api" }
revive-runner = { version = "0.1.0-dev.14", path = "crates/runner" } revive-runner = { version = "0.1.0-dev.16", path = "crates/runner" }
revive-solc-json-interface = { version = "0.1.0-dev.14", path = "crates/solc-json-interface" } revive-solc-json-interface = { version = "0.1.0-dev.16", path = "crates/solc-json-interface" }
revive-solidity = { version = "0.1.0-dev.14", path = "crates/solidity" } revive-solidity = { version = "0.1.0-dev.16", path = "crates/solidity" }
revive-stdlib = { version = "0.1.0-dev.14", path = "crates/stdlib" } revive-stdlib = { version = "0.1.0-dev.16", path = "crates/stdlib" }
revive-build-utils = { version = "0.1.0-dev.14", path = "crates/build-utils" } revive-build-utils = { version = "0.1.0-dev.16", path = "crates/build-utils" }
hex = "0.4.3" hex = "0.4.3"
cc = "1.2" cc = "1.2"
libc = "0.2.169" libc = "0.2.172"
tempfile = "3.17" tempfile = "3.17"
anyhow = "1.0" anyhow = "1.0"
semver = { version = "1.0", features = ["serde"] } semver = { version = "1.0", features = ["serde"] }
@@ -73,14 +73,13 @@ 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 = "c29e72a8628835e34deb6aa7db9a78a2e4eabcee" } polkadot-sdk = { version = "2503.0.1" }
# llvm # llvm
[workspace.dependencies.inkwell] [workspace.dependencies.inkwell]
git = "https://github.com/TheDan64/inkwell.git" version = "0.6.0"
rev = "7b410298b6a93450adaa90b1841d5805a3038f12"
default-features = false default-features = false
features = ["serde", "llvm18-0", "no-libffi-linking", "target-riscv"] features = ["serde", "llvm18-1", "no-libffi-linking", "target-riscv"]
[profile.bench] [profile.bench]
inherits = "release" inherits = "release"
+2 -2
View File
@@ -1,4 +1,4 @@
FROM rust:1.84.0 AS llvm-builder FROM rust:1.85.0 AS llvm-builder
WORKDIR /opt/revive WORKDIR /opt/revive
RUN apt update && \ RUN apt update && \
@@ -11,7 +11,7 @@ RUN make install-llvm-builder
RUN revive-llvm --target-env musl clone RUN revive-llvm --target-env musl clone
RUN revive-llvm --target-env musl build --llvm-projects lld --llvm-projects clang RUN revive-llvm --target-env musl build --llvm-projects lld --llvm-projects clang
FROM messense/rust-musl-cross@sha256:68b86bc7cb2867259e6b233415a665ff4469c28b57763e78c3bfea1c68091561 AS resolc-builder FROM messense/rust-musl-cross@sha256:c0154e992adb791c3b848dd008939d19862549204f8cb26f5ca7a00f629e6067 AS resolc-builder
WORKDIR /opt/revive WORKDIR /opt/revive
RUN apt update && \ RUN apt update && \
+4
View File
@@ -13,6 +13,10 @@ To create a new pre-release:
7. After the release is published, another workflow should start automatically and update json files in https://github.com/paritytech/resolc-bin. Check the changes. 7. After the release is published, another workflow should start automatically and update json files in https://github.com/paritytech/resolc-bin. Check the changes.
8. Update the [contract-docs](https://github.com/paritytech/contract-docs/) accordingly 8. Update the [contract-docs](https://github.com/paritytech/contract-docs/) accordingly
# `resolc` NPM package release
Will happen automatically.
# LLVM release # LLVM release
To create a new LLVM release, run "Release LLVM" workflow. Use current LLVM version as parameter, e.g. `18.1.8`. To create a new LLVM release, run "Release LLVM" workflow. Use current LLVM version as parameter, e.g. `18.1.8`.
-30
View File
@@ -1,30 +0,0 @@
# Known issues
The following is known and we are either working on it or it is a hard limitation. Please do not open a new issue.
## Release
`0.1.0-dev-2`
## Missing features
- [Libraries with public functions are not supported](https://github.com/paritytech/revive/issues/91)
- [Automatic import resolution is not supported](https://github.com/paritytech/revive/issues/98)
- The emulated EVM linear contract memory is limited to 64kb in size. Will be fixed with support for metered dynamic memory.
- [The contract calldata is currently limited to 1kb in size](https://github.com/paritytech/revive/issues/57)
- [EIP-4844 opcodes are not supported](https://github.com/paritytech/revive/issues/64)
- [Delegate calls are not supported](https://github.com/paritytech/revive/issues/67)
- [The `blockhash` opcode is not supported](https://github.com/paritytech/revive/issues/61)
- [The `extcodesize` opcode is not supported](https://github.com/paritytech/revive/issues/58)
- [The `origin` opcode is not supported](https://github.com/paritytech/revive/issues/59)
- [Gas limits for contract calls are ignored](https://github.com/paritytech/revive/issues/60)
- [Gas related opcodes are not supported](https://github.com/paritytech/revive/issues/60)
- IPFS metadata hashes are not supported
- [Compiled contract artifacts can exceed the pallet static memory limit and fail to deploy](https://github.com/paritytech/revive/issues/96).
- [Transfers to inexistant accounts will fail if the transferred value lies below the ED.](https://github.com/paritytech/revive/issues/83) Will be fixed in the pallet to make the ED completely transparent for contracts.
## Wontfix
Please consult our documentation to learn more about Solidity and EVM features likely to remain unsupported (and why they will not be supported).
TODO: Insert link to the relevant documentation section.
+2 -8
View File
@@ -117,13 +117,7 @@ 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")?;
crate::utils::check_presence("ninja")?;
let generator = if cfg!(target_os = "windows") {
"Visual Studio 17 2022"
} else {
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()?;
@@ -136,7 +130,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",
generator, "Ninja",
]) ])
.args(CMAKE_STATIC_ARGS) .args(CMAKE_STATIC_ARGS)
.args(cmake_dynamic_args(build_type, target_env)?) .args(cmake_dynamic_args(build_type, target_env)?)
+2 -1
View File
@@ -8,8 +8,9 @@ use std::path::Path;
use std::process::Command; use std::process::Command;
/// The build options shared by all platforms. /// The build options shared by all platforms.
pub const SHARED_BUILD_OPTS: [&str; 21] = [ pub const SHARED_BUILD_OPTS: [&str; 22] = [
"-DPACKAGE_VENDOR='Parity Technologies'", "-DPACKAGE_VENDOR='Parity Technologies'",
"-DCMAKE_CXX_FLAGS='-include cstdint -include stdint.h'",
"-DCMAKE_BUILD_WITH_INSTALL_RPATH=1", "-DCMAKE_BUILD_WITH_INSTALL_RPATH=1",
"-DLLVM_BUILD_DOCS='Off'", "-DLLVM_BUILD_DOCS='Off'",
"-DLLVM_INCLUDE_DOCS='Off'", "-DLLVM_INCLUDE_DOCS='Off'",
+1 -1
View File
@@ -38,7 +38,7 @@ pub const MUSL_SNAPSHOTS_URL: &str = "https://git.musl-libc.org/cgit/musl/snapsh
pub const EMSDK_SOURCE_URL: &str = "https://github.com/emscripten-core/emsdk.git"; pub const EMSDK_SOURCE_URL: &str = "https://github.com/emscripten-core/emsdk.git";
/// The emscripten SDK version. /// The emscripten SDK version.
pub const EMSDK_VERSION: &str = "3.1.64"; pub const EMSDK_VERSION: &str = "4.0.9";
/// The subprocess runner. /// The subprocess runner.
/// ///
-2
View File
@@ -5,7 +5,6 @@ use std::sync::OnceLock;
pub use self::debug_config::ir_type::IRType as DebugConfigIR; pub use self::debug_config::ir_type::IRType as DebugConfigIR;
pub use self::debug_config::DebugConfig; pub use self::debug_config::DebugConfig;
pub use self::memory::MemoryConfig;
pub use self::optimizer::settings::size_level::SizeLevel as OptimizerSettingsSizeLevel; pub use self::optimizer::settings::size_level::SizeLevel as OptimizerSettingsSizeLevel;
pub use self::optimizer::settings::Settings as OptimizerSettings; pub use self::optimizer::settings::Settings as OptimizerSettings;
pub use self::optimizer::Optimizer; pub use self::optimizer::Optimizer;
@@ -76,7 +75,6 @@ pub use self::target_machine::target::Target;
pub use self::target_machine::TargetMachine; pub use self::target_machine::TargetMachine;
pub(crate) mod debug_config; pub(crate) mod debug_config;
pub(crate) mod memory;
pub(crate) mod optimizer; pub(crate) mod optimizer;
pub(crate) mod polkavm; pub(crate) mod polkavm;
pub(crate) mod target_machine; pub(crate) mod target_machine;
@@ -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::cycles(), None => Self::size(),
}; };
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();
@@ -25,8 +25,8 @@ use inkwell::debug_info::AsDIScope;
use inkwell::debug_info::DIScope; use inkwell::debug_info::DIScope;
use inkwell::types::BasicType; use inkwell::types::BasicType;
use inkwell::values::BasicValue; use inkwell::values::BasicValue;
use revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory;
use crate::memory::MemoryConfig;
use crate::optimizer::settings::Settings as OptimizerSettings; use crate::optimizer::settings::Settings as OptimizerSettings;
use crate::optimizer::Optimizer; use crate::optimizer::Optimizer;
use crate::polkavm::DebugConfig; use crate::polkavm::DebugConfig;
@@ -88,7 +88,7 @@ where
/// The extra LLVM arguments that were used during target initialization. /// The extra LLVM arguments that were used during target initialization.
llvm_arguments: &'ctx [String], llvm_arguments: &'ctx [String],
/// The PVM memory configuration. /// The PVM memory configuration.
memory_config: MemoryConfig, memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
/// The project dependency manager. It can be any entity implementing the trait. /// The project dependency manager. It can be any entity implementing the trait.
/// The manager is used to get information about contracts and their dependencies during /// The manager is used to get information about contracts and their dependencies during
@@ -228,7 +228,7 @@ where
include_metadata_hash: bool, include_metadata_hash: bool,
debug_config: DebugConfig, debug_config: DebugConfig,
llvm_arguments: &'ctx [String], llvm_arguments: &'ctx [String],
memory_config: MemoryConfig, memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> Self { ) -> Self {
Self::set_data_layout(llvm, &module); Self::set_data_layout(llvm, &module);
Self::link_stdlib_module(llvm, &module); Self::link_stdlib_module(llvm, &module);
+3 -3
View File
@@ -7,12 +7,12 @@ pub mod evm;
pub use self::r#const::*; pub use self::r#const::*;
use crate::debug_config::DebugConfig; use crate::debug_config::DebugConfig;
use crate::memory::MemoryConfig;
use crate::optimizer::settings::Settings as OptimizerSettings; use crate::optimizer::settings::Settings as OptimizerSettings;
use anyhow::Context as AnyhowContext; use anyhow::Context as AnyhowContext;
use polkavm_common::program::ProgramBlob; use polkavm_common::program::ProgramBlob;
use polkavm_disassembler::{Disassembler, DisassemblyFormat}; use polkavm_disassembler::{Disassembler, DisassemblyFormat};
use revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory;
use sha3::Digest; use sha3::Digest;
use self::context::build::Build; use self::context::build::Build;
@@ -91,7 +91,7 @@ pub trait Dependency {
include_metadata_hash: bool, include_metadata_hash: bool,
debug_config: DebugConfig, debug_config: DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: MemoryConfig, memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<String>; ) -> anyhow::Result<String>;
/// Resolves a full contract path. /// Resolves a full contract path.
@@ -113,7 +113,7 @@ impl Dependency for DummyDependency {
_include_metadata_hash: bool, _include_metadata_hash: bool,
_debug_config: DebugConfig, _debug_config: DebugConfig,
_llvm_arguments: &[String], _llvm_arguments: &[String],
_memory_config: MemoryConfig, _memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<String> { ) -> anyhow::Result<String> {
Ok(String::new()) Ok(String::new())
} }
+7 -1
View File
@@ -94,6 +94,12 @@ impl FindAuthor<<Runtime as frame_system::Config>::AccountId> for Runtime {
where where
I: 'a + IntoIterator<Item = (frame_support::ConsensusEngineId, &'a [u8])>, I: 'a + IntoIterator<Item = (frame_support::ConsensusEngineId, &'a [u8])>,
{ {
Some([0xff; 32].into()) Some(
[[0xff; 20].as_slice(), [0xee; 12].as_slice()]
.concat()
.as_slice()
.try_into()
.unwrap(),
)
} }
} }
+1 -1
View File
@@ -9,7 +9,7 @@ description = "Implements the low level runtime API bindings with pallet contrac
[dependencies] [dependencies]
anyhow = { workspace = true } anyhow = { workspace = true }
inkwell = { workspace = true, features = ["target-riscv", "no-libffi-linking", "llvm18-0"] } inkwell = { workspace = true, features = ["target-riscv", "no-libffi-linking", "llvm18-1"] }
revive-common = { workspace = true } revive-common = { workspace = true }
+2
View File
@@ -8,6 +8,8 @@ pub use self::standard_json::input::language::Language as SolcStandardJsonInputL
pub use self::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata; pub use self::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata;
pub use self::standard_json::input::settings::metadata_hash::MetadataHash as SolcStandardJsonInputSettingsMetadataHash; pub use self::standard_json::input::settings::metadata_hash::MetadataHash as SolcStandardJsonInputSettingsMetadataHash;
pub use self::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer; pub use self::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
pub use self::standard_json::input::settings::polkavm::memory::MemoryConfig as SolcStandardJsonInputSettingsPolkaVMMemory;
pub use self::standard_json::input::settings::polkavm::PolkaVM as SolcStandardJsonInputSettingsPolkaVM;
pub use self::standard_json::input::settings::selection::file::flag::Flag as SolcStandardJsonInputSettingsSelectionFileFlag; pub use self::standard_json::input::settings::selection::file::flag::Flag as SolcStandardJsonInputSettingsSelectionFileFlag;
pub use self::standard_json::input::settings::selection::file::File as SolcStandardJsonInputSettingsSelectionFile; pub use self::standard_json::input::settings::selection::file::File as SolcStandardJsonInputSettingsSelectionFile;
pub use self::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection; pub use self::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection;
@@ -18,13 +18,14 @@ use crate::standard_json::input::settings::optimizer::Optimizer as SolcStandardJ
use crate::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection; use crate::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection;
#[cfg(feature = "resolc")] #[cfg(feature = "resolc")]
use crate::warning::Warning; use crate::warning::Warning;
use crate::SolcStandardJsonInputSettingsPolkaVM;
use self::language::Language; use self::language::Language;
use self::settings::Settings; use self::settings::Settings;
use self::source::Source; use self::source::Source;
/// The `solc --standard-json` input. /// The `solc --standard-json` input.
#[derive(Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Input { pub struct Input {
/// The input language. /// The input language.
@@ -63,6 +64,7 @@ impl Input {
optimizer: SolcStandardJsonInputSettingsOptimizer, optimizer: SolcStandardJsonInputSettingsOptimizer,
metadata: Option<SolcStandardJsonInputSettingsMetadata>, metadata: Option<SolcStandardJsonInputSettingsMetadata>,
#[cfg(feature = "resolc")] suppressed_warnings: Option<Vec<Warning>>, #[cfg(feature = "resolc")] suppressed_warnings: Option<Vec<Warning>>,
polkavm: Option<SolcStandardJsonInputSettingsPolkaVM>,
) -> anyhow::Result<Self> { ) -> anyhow::Result<Self> {
let mut paths: BTreeSet<PathBuf> = paths.iter().cloned().collect(); let mut paths: BTreeSet<PathBuf> = paths.iter().cloned().collect();
let libraries = Settings::parse_libraries(library_map)?; let libraries = Settings::parse_libraries(library_map)?;
@@ -90,6 +92,7 @@ impl Input {
output_selection, output_selection,
optimizer, optimizer,
metadata, metadata,
polkavm,
), ),
#[cfg(feature = "resolc")] #[cfg(feature = "resolc")]
suppressed_warnings, suppressed_warnings,
@@ -109,6 +112,7 @@ impl Input {
optimizer: SolcStandardJsonInputSettingsOptimizer, optimizer: SolcStandardJsonInputSettingsOptimizer,
metadata: Option<SolcStandardJsonInputSettingsMetadata>, metadata: Option<SolcStandardJsonInputSettingsMetadata>,
suppressed_warnings: Option<Vec<Warning>>, suppressed_warnings: Option<Vec<Warning>>,
polkavm: Option<SolcStandardJsonInputSettingsPolkaVM>,
) -> anyhow::Result<Self> { ) -> anyhow::Result<Self> {
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
let iter = sources.into_par_iter(); // Parallel iterator let iter = sources.into_par_iter(); // Parallel iterator
@@ -129,6 +133,7 @@ impl Input {
output_selection, output_selection,
optimizer, optimizer,
metadata, metadata,
polkavm,
), ),
suppressed_warnings, suppressed_warnings,
}) })
@@ -6,7 +6,7 @@ use serde::Serialize;
use crate::standard_json::input::settings::metadata_hash::MetadataHash; use crate::standard_json::input::settings::metadata_hash::MetadataHash;
/// The `solc --standard-json` input settings metadata. /// The `solc --standard-json` input settings metadata.
#[derive(Debug, Default, Serialize, Deserialize)] #[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Metadata { pub struct Metadata {
/// The bytecode hash mode. /// The bytecode hash mode.
@@ -3,6 +3,7 @@
pub mod metadata; pub mod metadata;
pub mod metadata_hash; pub mod metadata_hash;
pub mod optimizer; pub mod optimizer;
pub mod polkavm;
pub mod selection; pub mod selection;
use std::collections::BTreeMap; use std::collections::BTreeMap;
@@ -13,10 +14,11 @@ use serde::Serialize;
use self::metadata::Metadata; use self::metadata::Metadata;
use self::optimizer::Optimizer; use self::optimizer::Optimizer;
use self::polkavm::PolkaVM;
use self::selection::Selection; use self::selection::Selection;
/// The `solc --standard-json` input settings. /// The `solc --standard-json` input settings.
#[derive(Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Settings { pub struct Settings {
/// The target EVM version. /// The target EVM version.
@@ -43,6 +45,9 @@ pub struct Settings {
/// The metadata settings. /// The metadata settings.
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>, pub metadata: Option<Metadata>,
/// The resolc custom PolkaVM settings.
#[serde(skip_serializing)]
pub polkavm: Option<PolkaVM>,
} }
impl Settings { impl Settings {
@@ -54,6 +59,7 @@ impl Settings {
output_selection: Selection, output_selection: Selection,
optimizer: Optimizer, optimizer: Optimizer,
metadata: Option<Metadata>, metadata: Option<Metadata>,
polkavm: Option<PolkaVM>,
) -> Self { ) -> Self {
Self { Self {
evm_version, evm_version,
@@ -63,6 +69,7 @@ impl Settings {
optimizer, optimizer,
metadata, metadata,
via_ir: Some(true), via_ir: Some(true),
polkavm,
} }
} }
@@ -4,7 +4,7 @@ use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
/// The `solc --standard-json` input settings optimizer details. /// The `solc --standard-json` input settings optimizer details.
#[derive(Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Details { pub struct Details {
/// Whether the pass is enabled. /// Whether the pass is enabled.
@@ -8,7 +8,7 @@ use serde::Serialize;
use self::details::Details; use self::details::Details;
/// The `solc --standard-json` input settings optimizer. /// The `solc --standard-json` input settings optimizer.
#[derive(Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Optimizer { pub struct Optimizer {
/// Whether the optimizer is enabled. /// Whether the optimizer is enabled.
@@ -0,0 +1,26 @@
//! The `resolc --standard-json` polkavm settings.
//!
//! Used for options specific to PolkaVM which therefor don't exist in solc.
use memory::MemoryConfig;
use serde::{Deserialize, Serialize};
pub mod memory;
/// PVM specific compiler settings.
#[derive(Clone, Copy, Default, Debug, Serialize, Deserialize)]
pub struct PolkaVM {
/// The PolkaVM target machine memory configuration settings.
pub memory_config: MemoryConfig,
/// Instruct LLVM to emit debug information.
pub debug_information: bool,
}
impl PolkaVM {
pub fn new(memory_config: Option<MemoryConfig>, debug_information: bool) -> Self {
Self {
memory_config: memory_config.unwrap_or_default(),
debug_information,
}
}
}
@@ -10,7 +10,7 @@ use serde::Serialize;
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.
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct File { pub struct File {
/// The per-file output selections. /// The per-file output selections.
#[serde(rename = "", skip_serializing_if = "Option::is_none")] #[serde(rename = "", skip_serializing_if = "Option::is_none")]
@@ -10,7 +10,7 @@ use serde::Serialize;
use self::file::File as FileSelection; use self::file::File as FileSelection;
/// The `solc --standard-json` output selection. /// The `solc --standard-json` output selection.
#[derive(Debug, Serialize, Deserialize, Default, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq)]
pub struct Selection { pub struct Selection {
/// Only the 'all' wildcard is available for robustness reasons. /// Only the 'all' wildcard is available for robustness reasons.
#[serde(rename = "*", skip_serializing_if = "Option::is_none")] #[serde(rename = "*", skip_serializing_if = "Option::is_none")]
@@ -7,7 +7,7 @@ use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
/// The `solc --standard-json` input source. /// The `solc --standard-json` input source.
#[derive(Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Source { pub struct Source {
/// The source code file content. /// The source code file content.
+1 -1
View File
@@ -44,7 +44,7 @@ mimalloc = { version = "*", default-features = false }
[target.'cfg(target_os = "emscripten")'.dependencies] [target.'cfg(target_os = "emscripten")'.dependencies]
libc = { workspace = true } libc = { workspace = true }
inkwell = { workspace = true, features = ["target-riscv", "llvm18-0-no-llvm-linking"]} inkwell = { workspace = true, features = ["target-riscv", "llvm18-1-no-llvm-linking"]}
[build-dependencies] [build-dependencies]
git2 = { workspace = true, default-features = false } git2 = { workspace = true, default-features = false }
+15 -7
View File
@@ -45,6 +45,8 @@ use revive_solc_json_interface::ResolcWarning;
use revive_solc_json_interface::SolcStandardJsonInput; use revive_solc_json_interface::SolcStandardJsonInput;
use revive_solc_json_interface::SolcStandardJsonInputLanguage; use revive_solc_json_interface::SolcStandardJsonInputLanguage;
use revive_solc_json_interface::SolcStandardJsonInputSettingsOptimizer; use revive_solc_json_interface::SolcStandardJsonInputSettingsOptimizer;
use revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVM;
use revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory;
use revive_solc_json_interface::SolcStandardJsonInputSettingsSelection; use revive_solc_json_interface::SolcStandardJsonInputSettingsSelection;
/// Runs the Yul mode. /// Runs the Yul mode.
@@ -55,7 +57,7 @@ pub fn yul<T: Compiler>(
include_metadata_hash: bool, include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig, debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig, memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<Build> { ) -> anyhow::Result<Build> {
let path = match input_files.len() { let path = match input_files.len() {
1 => input_files.first().expect("Always exists"), 1 => input_files.first().expect("Always exists"),
@@ -94,7 +96,7 @@ pub fn llvm_ir(
include_metadata_hash: bool, include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig, debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig, memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<Build> { ) -> anyhow::Result<Build> {
let path = match input_files.len() { let path = match input_files.len() {
1 => input_files.first().expect("Always exists"), 1 => input_files.first().expect("Always exists"),
@@ -135,7 +137,7 @@ pub fn standard_output<T: Compiler>(
suppressed_warnings: Option<Vec<ResolcWarning>>, suppressed_warnings: Option<Vec<ResolcWarning>>,
debug_config: revive_llvm_context::DebugConfig, debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig, memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<Build> { ) -> anyhow::Result<Build> {
let solc_version = solc.version()?; let solc_version = solc.version()?;
@@ -154,6 +156,10 @@ pub fn standard_output<T: Compiler>(
), ),
None, None,
suppressed_warnings, suppressed_warnings,
Some(SolcStandardJsonInputSettingsPolkaVM::new(
Some(memory_config),
debug_config.emit_debug_info,
)),
)?; )?;
let source_code_files = solc_input let source_code_files = solc_input
@@ -208,9 +214,8 @@ pub fn standard_json<T: Compiler>(
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, mut debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let solc_version = solc.version()?; let solc_version = solc.version()?;
@@ -224,6 +229,9 @@ pub fn standard_json<T: Compiler>(
let optimizer_settings = let optimizer_settings =
revive_llvm_context::OptimizerSettings::try_from(&solc_input.settings.optimizer)?; revive_llvm_context::OptimizerSettings::try_from(&solc_input.settings.optimizer)?;
let polkavm_settings = solc_input.settings.polkavm.unwrap_or_default();
debug_config.emit_debug_info = polkavm_settings.debug_information;
let include_metadata_hash = match solc_input.settings.metadata { let include_metadata_hash = match solc_input.settings.metadata {
Some(ref metadata) => metadata.bytecode_hash != Some(MetadataHash::None), Some(ref metadata) => metadata.bytecode_hash != Some(MetadataHash::None),
None => true, None => true,
@@ -258,7 +266,7 @@ pub fn standard_json<T: Compiler>(
include_metadata_hash, include_metadata_hash,
debug_config, debug_config,
llvm_arguments, llvm_arguments,
memory_config, polkavm_settings.memory_config,
)?; )?;
build.write_to_standard_json(&mut solc_output, &solc_version)?; build.write_to_standard_json(&mut solc_output, &solc_version)?;
} }
@@ -286,7 +294,7 @@ pub fn combined_json<T: Compiler>(
output_directory: Option<PathBuf>, output_directory: Option<PathBuf>,
overwrite: bool, overwrite: bool,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig, memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let build = standard_output( let build = standard_output(
input_files, input_files,
+3 -2
View File
@@ -1,6 +1,7 @@
//! Process for compiling a single compilation unit. //! Process for compiling a single compilation unit.
//! The input data. //! The input data.
use revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
@@ -23,7 +24,7 @@ pub struct Input {
/// The extra LLVM arguments give used for manual control. /// The extra LLVM arguments give used for manual control.
pub llvm_arguments: Vec<String>, pub llvm_arguments: Vec<String>,
/// The PVM memory configuration. /// The PVM memory configuration.
pub memory_config: revive_llvm_context::MemoryConfig, pub memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
} }
impl Input { impl Input {
@@ -35,7 +36,7 @@ impl Input {
optimizer_settings: revive_llvm_context::OptimizerSettings, optimizer_settings: revive_llvm_context::OptimizerSettings,
debug_config: revive_llvm_context::DebugConfig, debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: Vec<String>, llvm_arguments: Vec<String>,
memory_config: revive_llvm_context::MemoryConfig, memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> Self { ) -> Self {
Self { Self {
contract, contract,
+2 -1
View File
@@ -5,6 +5,7 @@ pub mod metadata;
use std::collections::HashSet; use std::collections::HashSet;
use revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use sha3::Digest; use sha3::Digest;
@@ -78,7 +79,7 @@ impl Contract {
include_metadata_hash: bool, include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig, debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig, memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<ContractBuild> { ) -> anyhow::Result<ContractBuild> {
let llvm = inkwell::context::Context::create(); let llvm = inkwell::context::Context::create();
let optimizer = revive_llvm_context::Optimizer::new(optimizer_settings); let optimizer = revive_llvm_context::Optimizer::new(optimizer_settings);
+2 -2
View File
@@ -67,7 +67,7 @@ impl Project {
include_metadata_hash: bool, include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig, debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig, memory_config: revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<Build> { ) -> anyhow::Result<Build> {
let project = self.clone(); let project = self.clone();
#[cfg(feature = "parallel")] #[cfg(feature = "parallel")]
@@ -321,7 +321,7 @@ impl revive_llvm_context::PolkaVMDependency for Project {
include_metadata_hash: bool, include_metadata_hash: bool,
debug_config: revive_llvm_context::DebugConfig, debug_config: revive_llvm_context::DebugConfig,
llvm_arguments: &[String], llvm_arguments: &[String],
memory_config: revive_llvm_context::MemoryConfig, memory_config: revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory,
) -> anyhow::Result<String> { ) -> anyhow::Result<String> {
let contract_path = project.resolve_path(identifier)?; let contract_path = project.resolve_path(identifier)?;
let contract = project let contract = project
+20 -4
View File
@@ -185,8 +185,8 @@ pub struct Arguments {
/// 1.Increasing the heap size will increase startup costs. /// 1.Increasing the heap size will increase startup costs.
/// 2.The heap size contributes to the total memory size a contract can use, /// 2.The heap size contributes to the total memory size a contract can use,
/// which includes the contracts code size /// which includes the contracts code size
#[arg(long = "heap-size", default_value = "65536")] #[arg(long = "heap-size")]
pub heap_size: u32, pub heap_size: Option<u32>,
/// The contracts total stack size in bytes. /// The contracts total stack size in bytes.
/// ///
@@ -200,8 +200,8 @@ pub struct Arguments {
/// 1.Increasing the heap size will increase startup costs. /// 1.Increasing the heap size will increase startup costs.
/// 2.The stack size contributes to the total memory size a contract can use, /// 2.The stack size contributes to the total memory size a contract can use,
/// which includes the contracts code size /// which includes the contracts code size
#[arg(long = "stack-size", default_value = "32768")] #[arg(long = "stack-size")]
pub stack_size: u32, pub stack_size: Option<u32>,
} }
impl Arguments { impl Arguments {
@@ -330,6 +330,22 @@ impl Arguments {
if self.metadata_hash.is_some() { if self.metadata_hash.is_some() {
anyhow::bail!("Metadata hash mode must specified in standard JSON input settings."); anyhow::bail!("Metadata hash mode must specified in standard JSON input settings.");
} }
if self.heap_size.is_some() {
anyhow::bail!(
"Heap size must be specified in standard JSON input polkavm memory settings."
);
}
if self.stack_size.is_some() {
anyhow::bail!(
"Stack size must be specified in standard JSON input polkavm memory settings."
);
}
if self.emit_source_debug_info {
anyhow::bail!(
"Debug info must be requested in standard JSON input polkavm settings."
);
}
} }
Ok(()) Ok(())
+9 -6
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::cycles(), None => revive_llvm_context::OptimizerSettings::size(),
}; };
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();
@@ -148,10 +148,14 @@ fn main_inner() -> anyhow::Result<()> {
None => true, None => true,
}; };
let memory_config = revive_llvm_context::MemoryConfig { let mut memory_config =
heap_size: arguments.heap_size, revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory::default();
stack_size: arguments.stack_size, if let Some(heap_size) = arguments.heap_size {
}; memory_config.heap_size = heap_size
}
if let Some(stack_size) = arguments.stack_size {
memory_config.stack_size = stack_size
}
let build = if arguments.yul { let build = if arguments.yul {
revive_solidity::yul( revive_solidity::yul(
@@ -181,7 +185,6 @@ fn main_inner() -> anyhow::Result<()> {
arguments.allow_paths, arguments.allow_paths,
debug_config, debug_config,
&arguments.llvm_arguments, &arguments.llvm_arguments,
memory_config,
)?; )?;
return Ok(()); return Ok(());
} else if let Some(format) = arguments.combined_json { } else if let Some(format) = arguments.combined_json {
+1 -1
View File
@@ -19,7 +19,7 @@ use self::version::Version;
pub const FIRST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 0); pub const FIRST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 0);
/// The last supported version of `solc`. /// The last supported version of `solc`.
pub const LAST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 29); pub const LAST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 30);
/// `--include-path` was introduced in solc `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 const FIRST_INCLUDE_PATH_VERSION: semver::Version = semver::Version::new(0, 8, 8); pub const FIRST_INCLUDE_PATH_VERSION: semver::Version = semver::Version::new(0, 8, 8);
+4
View File
@@ -98,6 +98,7 @@ pub fn build_solidity_with_options(
), ),
None, None,
None, None,
None,
)?; )?;
let mut output = solc.standard_json(input, None, vec![], None)?; let mut output = solc.standard_json(input, None, vec![], None)?;
@@ -162,6 +163,7 @@ pub fn build_solidity_with_options_evm(
), ),
None, None,
None, None,
None,
)?; )?;
let mut output = solc.standard_json(input, None, vec![], None)?; let mut output = solc.standard_json(input, None, vec![], None)?;
@@ -213,6 +215,7 @@ pub fn build_solidity_and_detect_missing_libraries(
SolcStandardJsonInputSettingsOptimizer::new(true, None, &solc_version.default, false), SolcStandardJsonInputSettingsOptimizer::new(true, None, &solc_version.default, false),
None, None,
None, None,
None,
)?; )?;
let mut output = solc.standard_json(input, None, vec![], None)?; let mut output = solc.standard_json(input, None, vec![], None)?;
@@ -286,6 +289,7 @@ pub fn check_solidity_warning(
SolcStandardJsonInputSettingsOptimizer::new(true, None, &solc_version.default, false), SolcStandardJsonInputSettingsOptimizer::new(true, None, &solc_version.default, false),
None, None,
suppressed_warnings, suppressed_warnings,
None,
)?; )?;
let output = solc.standard_json(input, None, vec![], None)?; let output = solc.standard_json(input, None, vec![], None)?;
@@ -1,19 +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(pathToContracts, 'yul', contractYulFilename); const pathToBasicYulContract = path.join(
const pathToBasicSolContract = path.join(pathToContracts, 'solidity', contractSolFilename); pathToContracts,
const pathToSolBinOutputFile = path.join(pathToOutputDir, contractSolFilename + binExtension); 'yul',
const pathToSolAsmOutputFile = path.join(pathToOutputDir, contractSolFilename + asmExtension); contractYulFilename
)
const pathToBasicSolContract = path.join(
pathToContracts,
'solidity',
contractSolFilename
)
const pathToSolBinOutputFile = path.join(
pathToOutputDir,
contractSolFilename + binExtension
)
const pathToSolAsmOutputFile = path.join(
pathToOutputDir,
contractSolFilename + asmExtension
)
export const paths = { export const paths = {
outputDir: outputDir, outputDir: outputDir,
@@ -30,4 +44,4 @@ export const paths = {
pathToBasicYulContract: pathToBasicYulContract, pathToBasicYulContract: pathToBasicYulContract,
pathToSolBinOutputFile: pathToSolBinOutputFile, pathToSolBinOutputFile: pathToSolBinOutputFile,
pathToSolAsmOutputFile: pathToSolAsmOutputFile, pathToSolAsmOutputFile: pathToSolAsmOutputFile,
}; }
@@ -1,44 +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 = (command: string, stdin?: string): CommandResult => { export const executeCommand = (
if (stdin) { command: string,
const process = spawnSync(command, [], { stdin?: string
input: stdin, ): CommandResult => {
shell: true, if (stdin) {
encoding: 'utf8', const process = spawnSync(command, [], {
maxBuffer: 30 * 1024 * 1024 input: stdin,
}); shell: true,
encoding: 'utf8',
maxBuffer: 30 * 1024 * 1024,
})
return {
exitCode: process.status || 0,
output: (process.stdout || process.stderr || '').toString()
};
}
const result = shell.exec(command, { silent: true, async: false });
return { return {
exitCode: result.code, exitCode: process.status || 0,
output: result.stdout || result.stderr || '' output: (process.stdout || process.stderr || '').toString(),
}; }
}; }
const result = shell.exec(command, { silent: true, async: false })
return {
exitCode: result.code,
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 = (pathToFileDir: string, fileName: string, fileExtension: string): boolean => { export const isFileExist = (
return shell.ls(pathToFileDir).stdout.includes(fileName + fileExtension); pathToFileDir: string,
}; fileName: string,
fileExtension: string
): boolean => {
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,43 +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(/(No input sources specified|Compilation aborted)/i); expect(resultInvalid.output).toMatch(
}); /(No input sources specified|Compilation aborted)/i
)
it("Invalid command exit code = 1", () => { })
expect(resultInvalid.exitCode).toBe(1);
});
it("Invalid solc exit code == Invalid resolc exit code", () => { it('Invalid command exit code = 1', () => {
const command = 'solc --asm'; expect(resultInvalid.exitCode).toBe(1)
const solcResult = executeCommand(command); })
expect(solcResult.exitCode).toBe(resultInvalid.exitCode);
}); it('Invalid solc exit code == Invalid resolc exit code', () => {
}); const command = 'solc --asm'
const solcResult = executeCommand(command)
expect(solcResult.exitCode).toBe(resultInvalid.exitCode)
})
})
@@ -1,191 +1,241 @@
import { executeCommand, isFolderExist, isFileExist, isFileEmpty } from "../src/helper"; import {
import { paths } from '../src/entities'; executeCommand,
import * as shell from 'shelljs'; isFolderExist,
import * as path from 'path'; isFileExist,
isFileEmpty,
} from '../src/helper'
import { paths } from '../src/entities'
import * as shell from 'shelljs'
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", () => {
const command = 'solc';
const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
it('solc exit code == resolc exit code', () => {
const command = 'solc'
const solcResult = executeCommand(command)
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 result = executeCommand(command)
const command = `resolc ${paths.pathToBasicSolContract} --overwrite -O3 --bin --output-dir "${paths.pathToOutputDir}"`; // potential issue on resolc with full path on Windows cmd it('Compiler run successful', () => {
const result = executeCommand(command); expect(result.output).toMatch(/(Compiler run successful.)/i)
})
it("Compiler run successful", () => { it('Exit code = 0', () => {
expect(result.output).toMatch(/(Compiler run successful.)/i); expect(result.exitCode).toBe(0)
}); })
it("Exit code = 0", () => { it('Output dir is created', () => {
expect(result.exitCode).toBe(0); expect(isFolderExist(paths.pathToOutputDir)).toBe(true)
}); })
it("Output dir is created", () => { xit('Output file is created', () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true); // a bug on windows
}); expect(
xit("Output file is created", () => { // a bug on windows isFileExist(
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.binExtension)).toBe(true); paths.pathToOutputDir,
}); paths.contractSolFilename,
it("the output file is not empty", () => { paths.binExtension
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false); )
}); ).toBe(true)
it("No 'Error'/'Warning'/'Fail' in the output", () => { })
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i); it('the output file is not empty', () => {
}); expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false)
}); })
it("No 'Error'/'Warning'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i)
})
})
//#1818 //#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 result = executeCommand(command)
const command = `resolc ${paths.pathToBasicSolContract} --overwrite -O3 --bin --asm --output-dir "${paths.pathToOutputDir}"`; // potential issue on resolc with full path on Windows cmd it('Compiler run successful', () => {
const result = executeCommand(command); expect(result.output).toMatch(/(Compiler run successful.)/i)
})
it('Exit code = 0', () => {
expect(result.exitCode).toBe(0)
})
it('Output dir is created', () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true)
})
xit('Output files are created', () => {
// a bug on windows
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractSolFilename,
paths.binExtension
)
).toBe(true)
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractSolFilename,
paths.asmExtension
)
).toBe(true)
})
it('the output files are not empty', () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false)
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false)
})
it("No 'Error'/'Warning'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i)
})
})
it("Compiler run successful", () => { describe('Run resolc with source debug information', () => {
expect(result.output).toMatch(/(Compiler run successful.)/i); const commands = [
}); `resolc -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`,
it("Exit code = 0", () => { `resolc --disable-solc-optimizer -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`,
expect(result.exitCode).toBe(0); ] // potential issue on resolc with full path on Windows cmd`;
});
it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
});
xit("Output files are created", () => { // a bug on windows
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.binExtension)).toBe(true);
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.asmExtension)).toBe(true);
});
it("the output files are not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
});
it("No 'Error'/'Warning'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ww]arning|[Ff]ail)/i);
});
});
describe("Run resolc with source debug information", () => { for (var idx in commands) {
const commands = [ const command = commands[idx]
`resolc -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`, const result = executeCommand(command)
`resolc --disable-solc-optimizer -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`
]; // potential issue on resolc with full path on Windows cmd`;
for (var idx in commands) { it('Compiler run successful', () => {
const command = commands[idx]; expect(result.output).toMatch(/(Compiler run successful.)/i)
const result = executeCommand(command); })
it('Exit code = 0', () => {
expect(result.exitCode).toBe(0)
})
it('Output dir is created', () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true)
})
it('Output files are created', () => {
// a bug on windows
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractSolFilename,
paths.binExtension
)
).toBe(true)
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractSolFilename,
paths.asmExtension
)
).toBe(true)
})
it('the output files are not empty', () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false)
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false)
})
it("No 'Error'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i)
})
}
})
it("Compiler run successful", () => { describe('Run resolc with source debug information, check LLVM debug-info', () => {
expect(result.output).toMatch(/(Compiler run successful.)/i); const commands = [
}); `resolc -g ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`,
it("Exit code = 0", () => { `resolc -g --disable-solc-optimizer ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`,
expect(result.exitCode).toBe(0); ] // potential issue on resolc with full path on Windows cmd`;
});
it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
});
it("Output files are created", () => { // a bug on windows
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.binExtension)).toBe(true);
expect(isFileExist(paths.pathToOutputDir, paths.contractSolFilename, paths.asmExtension)).toBe(true);
});
it("the output files are not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
});
it("No 'Error'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i);
});
}
});
describe("Run resolc with source debug information, check LLVM debug-info", () => { for (var idx in commands) {
const commands = [ const command = commands[idx]
`resolc -g ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`, const result = executeCommand(command)
`resolc -g --disable-solc-optimizer ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`
]; // potential issue on resolc with full path on Windows cmd`;
for (var idx in commands) { it('Compiler run successful', () => {
const command = commands[idx]; expect(result.output).toMatch(/(Compiler run successful.)/i)
const result = executeCommand(command); })
it('Exit code = 0', () => {
expect(result.exitCode).toBe(0)
})
it('Output dir is created', () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true)
})
it('Output files are created', () => {
// a bug on windows
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractOptimizedLLVMFilename,
paths.llvmExtension
)
).toBe(true)
expect(
isFileExist(
paths.pathToOutputDir,
paths.contractUnoptimizedLLVMFilename,
paths.llvmExtension
)
).toBe(true)
})
it('the output files are not empty', () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false)
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false)
})
it("No 'Error'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i)
})
}
})
it("Compiler run successful", () => { describe('Standard JSON compilation with path options', () => {
expect(result.output).toMatch(/(Compiler run successful.)/i); const contractsDir = path.join(shell.tempdir(), 'contracts-test')
}); const inputFile = path.join(__dirname, '..', 'src/contracts/compiled/1.json')
it("Exit code = 0", () => {
expect(result.exitCode).toBe(0);
});
it("Output dir is created", () => {
expect(isFolderExist(paths.pathToOutputDir)).toBe(true);
});
it("Output files are created", () => { // a bug on windows
expect(isFileExist(paths.pathToOutputDir, paths.contractOptimizedLLVMFilename, paths.llvmExtension)).toBe(true);
expect(isFileExist(paths.pathToOutputDir, paths.contractUnoptimizedLLVMFilename, paths.llvmExtension)).toBe(true);
});
it("the output files are not empty", () => {
expect(isFileEmpty(paths.pathToSolBinOutputFile)).toBe(false);
expect(isFileEmpty(paths.pathToSolAsmOutputFile)).toBe(false);
});
it("No 'Error'/'Fail' in the output", () => {
expect(result.output).not.toMatch(/([Ee]rror|[Ff]ail)/i);
});
}
});
beforeAll(() => {
shell.mkdir('-p', contractsDir)
const input = JSON.parse(shell.cat(inputFile).toString())
describe("Standard JSON compilation with path options", () => { Object.entries(input.sources).forEach(
const contractsDir = path.join(shell.tempdir(), 'contracts-test'); ([sourcePath, source]: [string, any]) => {
const inputFile = path.join(__dirname, '..', 'src/contracts/compiled/1.json'); const filePath = path.join(contractsDir, sourcePath)
shell.mkdir('-p', path.dirname(filePath))
shell.ShellString(source.content).to(filePath)
}
)
})
afterAll(() => {
shell.rm('-rf', contractsDir)
})
describe('Output with all path options', () => {
let result: { exitCode: number; output: string }
beforeAll(() => { beforeAll(() => {
shell.mkdir('-p', contractsDir); const tempInputFile = path.join(contractsDir, 'temp-input.json')
shell.cp(inputFile, tempInputFile)
const inputContent = shell.cat(inputFile).toString()
const input = JSON.parse(shell.cat(inputFile).toString()); const command = `resolc --standard-json --base-path "${contractsDir}" --include-path "${contractsDir}" --allow-paths "${contractsDir}"`
Object.entries(input.sources).forEach(([sourcePath, source]: [string, any]) => { result = executeCommand(command, inputContent)
const filePath = path.join(contractsDir, sourcePath);
shell.mkdir('-p', path.dirname(filePath));
shell.ShellString(source.content).to(filePath);
});
});
afterAll(() => { shell.rm(tempInputFile)
shell.rm('-rf', contractsDir); })
});
describe("Output with all path options", () => { it('Compiler run successful without emiting warnings', () => {
let result: { exitCode: number; output: string }; const parsedResults = JSON.parse(result.output)
expect(
beforeAll(() => { parsedResults.errors.filter(
const tempInputFile = path.join(contractsDir, 'temp-input.json'); (error: { type: string }) => error.type != 'Warning'
shell.cp(inputFile, tempInputFile); )
const inputContent = shell.cat(inputFile).toString(); ).toEqual([])
})
const command = `resolc --standard-json --base-path "${contractsDir}" --include-path "${contractsDir}" --allow-paths "${contractsDir}"`; })
})
result = executeCommand(command, inputContent);
shell.rm(tempInputFile);
});
it("Compiler run successful without emiting warnings", () => {
const parsedResults = JSON.parse(result.output)
expect(parsedResults.errors.filter((error: { type: string; }) => error.type != 'Warning')).toEqual([]);
});
});
});
@@ -1,40 +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', () => {
// unknown solc issue for datatype of the contract
const command = `solc ${paths.pathToBasicSolContract} --yul`
const solcResult = executeCommand(command)
expect(solcResult.exitCode).toBe(result.exitCode)
})
xit("solc exit code == resolc exit code", () => { // unknown solc issue for datatype of the contract it('run invalid: resolc --yul', () => {
const command = `solc ${paths.pathToBasicSolContract} --yul`; expect(resultInvalid.output).toMatch(/(The input file is missing)/i)
const solcResult = executeCommand(command); })
expect(solcResult.exitCode).toBe(result.exitCode); it('Invalid command exit code = 1', () => {
}); expect(resultInvalid.exitCode).toBe(1)
})
it("run invalid: resolc --yul", () => { it('Invalid solc exit code == Invalid resolc exit code', () => {
expect(resultInvalid.output).toMatch(/(The input file is missing)/i); const command = 'solc --yul'
}); const solcResult = executeCommand(command)
it("Invalid command exit code = 1", () => { expect(solcResult.exitCode).toBe(resultInvalid.exitCode)
expect(resultInvalid.exitCode).toBe(1); })
}); })
it("Invalid solc exit code == Invalid resolc exit code", () => {
const command = 'solc --yul';
const solcResult = executeCommand(command);
expect(solcResult.exitCode).toBe(resultInvalid.exitCode);
});
});
+159 -124
View File
@@ -1,153 +1,188 @@
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', () => {
const zksolcCommand = 'zksolc'
const solcCommand = 'solc'
const json_args: string[] = [
`abi`,
`hashes`,
`metadata`,
`devdoc`,
`userdoc`,
`storage-layout`,
`ast`,
`asm`,
`bin`,
`bin-runtime`,
]
describe("Set of --combined-json tests", () => { //id1742:I
const zksolcCommand = 'zksolc'; describe(`Run ${zksolcCommand} with just --combined-json`, () => {
const solcCommand = 'solc'; const args = [`--combined-json`]
const json_args: string[] = [`abi`, `hashes`, `metadata`, `devdoc`, `userdoc`, `storage-layout`, `ast`, `asm`, `bin`, `bin-runtime`]; const result = executeCommand(zksolcCommand, args)
//id1742:I it('Valid command exit code = 1', () => {
describe(`Run ${zksolcCommand} with just --combined-json`, () => { expect(result.exitCode).toBe(1)
const args = [`--combined-json`]; })
const result = executeCommand(zksolcCommand, args);
it("Valid command exit code = 1", () => { it('--combined-json error is presented', () => {
expect(result.exitCode).toBe(1); expect(result.output).toMatch(/(requires a value but none was supplied)/i)
}); })
it("--combined-json error is presented", () => { it('solc exit code == zksolc exit code', () => {
expect(result.output).toMatch(/(requires a value but none was supplied)/i); const solcResult = executeCommand(solcCommand, args)
}); expect(solcResult.exitCode).toBe(result.exitCode)
})
})
it("solc exit code == zksolc exit code", () => { //id1742:II
const solcResult = executeCommand(solcCommand, args); describe(`Run ${zksolcCommand} with Sol contract and --combined-json`, () => {
expect(solcResult.exitCode).toBe(result.exitCode); const args = [`${paths.pathToBasicSolContract}`, `--combined-json`]
}); const result = executeCommand(zksolcCommand, args)
});
//id1742:II it('Valid command exit code = 1', () => {
describe(`Run ${zksolcCommand} with Sol contract and --combined-json`, () => { expect(result.exitCode).toBe(1)
const args = [`${paths.pathToBasicSolContract}`, `--combined-json`]; })
const result = executeCommand(zksolcCommand, args);
it("Valid command exit code = 1", () => { it('--combined-json error is presented', () => {
expect(result.exitCode).toBe(1); expect(result.output).toMatch(/(requires a value but none was supplied)/i)
}); })
it("--combined-json error is presented", () => { it('solc exit code == zksolc exit code', () => {
expect(result.output).toMatch(/(requires a value but none was supplied)/i); const solcResult = executeCommand(solcCommand, args)
}); expect(solcResult.exitCode).toBe(result.exitCode)
})
})
it("solc exit code == zksolc exit code", () => { //id1742:III
const solcResult = executeCommand(solcCommand, args); for (let i = 0; i < json_args.length; i++) {
expect(solcResult.exitCode).toBe(result.exitCode); 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)
//id1742:III it('Valid command exit code = 0', () => {
for (let i = 0; i < json_args.length; i++) { expect(result.exitCode).toBe(0)
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", () => { it('--combined-json error is presented', () => {
expect(result.exitCode).toBe(0); expect(result.output).toMatch(/(contracts)/i)
}); })
it("--combined-json error is presented", () => { it('solc exit code == zksolc exit code', () => {
expect(result.output).toMatch(/(contracts)/i); const solcResult = executeCommand(solcCommand, args)
}); expect(solcResult.exitCode).toBe(result.exitCode)
})
})
}
it("solc exit code == zksolc exit code", () => { //id1829:I
const solcResult = executeCommand(solcCommand, args); for (let i = 0; i < json_args.length; i++) {
expect(solcResult.exitCode).toBe(result.exitCode); 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)
//id1829:I it('Valid command exit code = 1', () => {
for (let i = 0; i < json_args.length; i++) { expect(result.exitCode).toBe(1)
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", () => { it('--combined-json error is presented', () => {
expect(result.exitCode).toBe(1); expect(result.output).toMatch(/(Invalid option|error)/i)
}); })
it("--combined-json error is presented", () => { it('solc exit code == zksolc exit code', () => {
expect(result.output).toMatch(/(Invalid option|error)/i); const solcResult = executeCommand(solcCommand, args)
}); expect(solcResult.exitCode).toBe(result.exitCode)
})
})
}
it("solc exit code == zksolc exit code", () => { //id1829:II
const solcResult = executeCommand(solcCommand, args); for (let i = 0; i < json_args.length; i++) {
expect(solcResult.exitCode).toBe(result.exitCode); 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)
//id1829:II xit('Valid command exit code = 1', () => {
for (let i = 0; i < json_args.length; i++) { expect(result.exitCode).toBe(1)
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", () => { it('--combined-json error is presented', () => {
expect(result.exitCode).toBe(1); expect(result.output).toMatch(
}); /(No such file or directory|cannot find the file specified)/i
) // Hopefully we should have more precise message here!
})
it("--combined-json error is presented", () => { xit('solc exit code == zksolc exit code', () => {
expect(result.output).toMatch(/(No such file or directory|cannot find the file specified)/i); // Hopefully we should have more precise message here! const solcResult = executeCommand(solcCommand, args)
}); expect(solcResult.exitCode).toBe(result.exitCode)
})
})
}
xit("solc exit code == zksolc exit code", () => { //id1829:III
const solcResult = executeCommand(solcCommand, args); for (let i = 0; i < json_args.length; i++) {
expect(solcResult.exitCode).toBe(result.exitCode); 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)
//id1829:III it('Valid command exit code = 1', () => {
for (let i = 0; i < json_args.length; i++) { expect(result.exitCode).toBe(1)
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", () => { it('--combined-json error is presented', () => {
expect(result.exitCode).toBe(1); expect(result.output).toMatch(/(cannot be used multiple times)/i)
}); })
it("--combined-json error is presented", () => { it('solc exit code == zksolc exit code', () => {
expect(result.output).toMatch(/(cannot be used multiple times)/i); const solcResult = executeCommand(solcCommand, args)
}); expect(solcResult.exitCode).toBe(result.exitCode)
})
})
}
it("solc exit code == zksolc exit code", () => { //id1830
const solcResult = executeCommand(solcCommand, args); for (let i = 0; i < json_args.length; i++) {
expect(solcResult.exitCode).toBe(result.exitCode); 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)
//id1830 it('Valid command exit code = 1', () => {
for (let i = 0; i < json_args.length; i++) { expect(result.exitCode).toBe(1)
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", () => { it('--combined-json error is presented', () => {
expect(result.exitCode).toBe(1); expect(result.output).toMatch(/(ParserError: Expected identifier)/i)
}); })
asd
it("--combined-json error is presented", () => { it('solc exit code == zksolc exit code', () => {
expect(result.output).toMatch(/(ParserError: Expected identifier)/i); const solcResult = executeCommand(solcCommand, args)
}); expect(solcResult.exitCode).toBe(result.exitCode)
asd })
})
it("solc exit code == zksolc exit code", () => { }
const solcResult = executeCommand(solcCommand, args); })
expect(solcResult.exitCode).toBe(result.exitCode);
});
});
}
});
+1 -1
View File
@@ -7,7 +7,7 @@ repository.workspace = true
description = "revive compiler stdlib components" description = "revive compiler stdlib components"
[dependencies] [dependencies]
inkwell = { workspace = true, features = ["target-riscv", "no-libffi-linking", "llvm18-0"] } inkwell = { workspace = true, features = ["target-riscv", "no-libffi-linking", "llvm18-1"] }
[build-dependencies] [build-dependencies]
revive-build-utils = { workspace = true } revive-build-utils = { workspace = true }
+4 -4
View File
@@ -4,8 +4,8 @@ import tseslint from 'typescript-eslint'
/** @type {import('eslint').Linter.Config[]} */ /** @type {import('eslint').Linter.Config[]} */
export default [ export default [
{ files: ['**/*.{mjs,ts}'] }, { files: ['**/*.{mjs,ts}'] },
{ languageOptions: { globals: globals.browser } }, { languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended, pluginJs.configs.recommended,
...tseslint.configs.recommended, ...tseslint.configs.recommended,
] ]
+1 -1
View File
@@ -3,7 +3,7 @@ const path = require("path");
const { minify } = require("terser"); const { minify } = require("terser");
const SOLJSON_URI = const SOLJSON_URI =
"https://binaries.soliditylang.org/wasm/soljson-v0.8.29+commit.ab55807c.js"; "https://binaries.soliditylang.org/wasm/soljson-v0.8.30+commit.73712a01.js";
const RESOLC_WASM_URI = const RESOLC_WASM_URI =
process.env.RELEASE_RESOLC_WASM_URI || "http://127.0.0.1:8080/resolc.wasm"; process.env.RELEASE_RESOLC_WASM_URI || "http://127.0.0.1:8080/resolc.wasm";
const RESOLC_WASM_TARGET_DIR = path.join( const RESOLC_WASM_TARGET_DIR = path.join(
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "revive", "name": "revive",
"private": true, "private": true,
"dependencies": { "dependencies": {
"solc": "^0.8.29" "solc": ">=0.8.0 <=0.8.30"
}, },
"scripts": { "scripts": {
"example:web": "http-server ./examples/web/", "example:web": "http-server ./examples/web/",
+106 -110
View File
@@ -1,142 +1,138 @@
import { expect } from "chai"; import { expect } from 'chai'
import { compile } from "../examples/node/revive.js"; import { compile } from '../examples/node/revive.js'
import { fileURLToPath } from "url"; import { fileURLToPath } from 'url'
import path from "path"; import path from 'path'
import fs from "fs"; import fs from 'fs'
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename)
function loadFixture(fixture) { function loadFixture(fixture) {
const fixturePath = path.resolve(__dirname, `../fixtures/${fixture}`); const fixturePath = path.resolve(__dirname, `../fixtures/${fixture}`)
return JSON.parse(fs.readFileSync(fixturePath, "utf-8")); return JSON.parse(fs.readFileSync(fixturePath, 'utf-8'))
} }
describe("Compile Function Tests", function () { describe('Compile Function Tests', function () {
it("should successfully compile valid Solidity code", async function () { it('should successfully compile valid Solidity code', async function () {
const standardInput = loadFixture("storage.json"); const standardInput = loadFixture('storage.json')
const result = await compile(standardInput); const result = await compile(standardInput)
expect(result).to.be.a("string"); expect(result).to.be.a('string')
const output = JSON.parse(result); const output = JSON.parse(result)
expect(output).to.have.property("contracts"); expect(output).to.have.property('contracts')
expect(output.contracts["fixtures/storage.sol"]).to.have.property( expect(output.contracts['fixtures/storage.sol']).to.have.property('Storage')
"Storage", expect(output.contracts['fixtures/storage.sol'].Storage).to.have.property(
); 'abi'
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).to.have.property( )
"evm",
);
expect( expect(
output.contracts["fixtures/storage.sol"].Storage.evm, output.contracts['fixtures/storage.sol'].Storage.evm
).to.have.property("bytecode"); ).to.have.property('bytecode')
}); })
if (typeof globalThis.Bun == "undefined") { if (typeof globalThis.Bun == 'undefined') {
// Running this test with Bun on a Linux host causes: // Running this test with Bun on a Linux host causes:
// RuntimeError: Out of bounds memory access (evaluating 'getWasmTableEntry(index)(a1, a2, a3, a4, a5)') // 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. // Once this issue is resolved, the test will be re-enabled.
it("should successfully compile large Solidity code", async function () { it('should successfully compile large Solidity code', async function () {
const standardInput = loadFixture("token.json"); const standardInput = loadFixture('token.json')
const result = await compile(standardInput); const result = await compile(standardInput)
expect(result).to.be.a("string"); expect(result).to.be.a('string')
const output = JSON.parse(result); const output = JSON.parse(result)
expect(output).to.have.property("contracts"); expect(output).to.have.property('contracts')
expect(output.contracts["fixtures/token.sol"]).to.have.property( expect(output.contracts['fixtures/token.sol']).to.have.property('MyToken')
"MyToken", expect(output.contracts['fixtures/token.sol'].MyToken).to.have.property(
); 'abi'
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).to.have.property( )
"evm",
);
expect( expect(
output.contracts["fixtures/token.sol"].MyToken.evm, output.contracts['fixtures/token.sol'].MyToken.evm
).to.have.property("bytecode"); ).to.have.property('bytecode')
}); })
it("should successfully compile a valid Solidity contract that instantiates the token contracts", async function () { it('should successfully compile a valid Solidity contract that instantiates the token contracts', async function () {
const standardInput = loadFixture("instantiate_tokens.json"); const standardInput = loadFixture('instantiate_tokens.json')
const result = await compile(standardInput); const result = await compile(standardInput)
expect(result).to.be.a("string"); expect(result).to.be.a('string')
const output = JSON.parse(result); const output = JSON.parse(result)
expect(output).to.have.property("contracts"); expect(output).to.have.property('contracts')
expect( expect(
output.contracts["fixtures/instantiate_tokens.sol"], output.contracts['fixtures/instantiate_tokens.sol']
).to.have.property("TokensFactory"); ).to.have.property('TokensFactory')
expect( expect(
output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory, output.contracts['fixtures/instantiate_tokens.sol'].TokensFactory
).to.have.property("abi"); ).to.have.property('abi')
expect( expect(
output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory, output.contracts['fixtures/instantiate_tokens.sol'].TokensFactory
).to.have.property("evm"); ).to.have.property('evm')
expect( expect(
output.contracts["fixtures/instantiate_tokens.sol"].TokensFactory.evm, output.contracts['fixtures/instantiate_tokens.sol'].TokensFactory.evm
).to.have.property("bytecode"); ).to.have.property('bytecode')
}); })
} }
it("should throw an error for invalid Solidity code", async function () { it('should throw an error for invalid Solidity code', async function () {
const standardInput = loadFixture("invalid_contract_content.json"); const standardInput = loadFixture('invalid_contract_content.json')
const result = await compile(standardInput); const result = await compile(standardInput)
expect(result).to.be.a("string"); expect(result).to.be.a('string')
const output = JSON.parse(result); const output = JSON.parse(result)
expect(output).to.have.property("errors"); expect(output).to.have.property('errors')
expect(output.errors).to.be.an("array"); expect(output.errors).to.be.an('array')
expect(output.errors.length).to.be.greaterThan(0); expect(output.errors.length).to.be.greaterThan(0)
expect(output.errors[0].type).to.exist; expect(output.errors[0].type).to.exist
expect(output.errors[0].type).to.contain("ParserError"); expect(output.errors[0].type).to.contain('ParserError')
}); })
it("should return not found error for missing imports", async function () { it('should return not found error for missing imports', async function () {
const standardInput = loadFixture("missing_import.json"); const standardInput = loadFixture('missing_import.json')
const result = await compile(standardInput); const result = await compile(standardInput)
const output = JSON.parse(result); const output = JSON.parse(result)
expect(output).to.have.property("errors"); expect(output).to.have.property('errors')
expect(output.errors).to.be.an("array"); expect(output.errors).to.be.an('array')
expect(output.errors.length).to.be.greaterThan(0); expect(output.errors.length).to.be.greaterThan(0)
expect(output.errors[0].message).to.exist; expect(output.errors[0].message).to.exist
expect(output.errors[0].message).to.include( expect(output.errors[0].message).to.include(
'Source "nonexistent/console.sol" not found', 'Source "nonexistent/console.sol" not found'
); )
}); })
it("should successfully compile a valid Solidity contract that instantiates another contract", async function () { it('should successfully compile a valid Solidity contract that instantiates another contract', async function () {
const standardInput = loadFixture("instantiate.json"); const standardInput = loadFixture('instantiate.json')
const result = await compile(standardInput); const result = await compile(standardInput)
expect(result).to.be.a("string"); expect(result).to.be.a('string')
const output = JSON.parse(result); const output = JSON.parse(result)
expect(output).to.have.property("contracts"); expect(output).to.have.property('contracts')
expect(output.contracts["fixtures/instantiate.sol"]).to.have.property( expect(output.contracts['fixtures/instantiate.sol']).to.have.property(
"ChildContract", 'ChildContract'
); )
expect( expect(
output.contracts["fixtures/instantiate.sol"].ChildContract, output.contracts['fixtures/instantiate.sol'].ChildContract
).to.have.property("abi"); ).to.have.property('abi')
expect( expect(
output.contracts["fixtures/instantiate.sol"].ChildContract, output.contracts['fixtures/instantiate.sol'].ChildContract
).to.have.property("evm"); ).to.have.property('evm')
expect( expect(
output.contracts["fixtures/instantiate.sol"].ChildContract.evm, output.contracts['fixtures/instantiate.sol'].ChildContract.evm
).to.have.property("bytecode"); ).to.have.property('bytecode')
expect(output.contracts["fixtures/instantiate.sol"]).to.have.property( expect(output.contracts['fixtures/instantiate.sol']).to.have.property(
"MainContract", 'MainContract'
); )
expect( expect(
output.contracts["fixtures/instantiate.sol"].MainContract, output.contracts['fixtures/instantiate.sol'].MainContract
).to.have.property("abi"); ).to.have.property('abi')
expect( expect(
output.contracts["fixtures/instantiate.sol"].MainContract, output.contracts['fixtures/instantiate.sol'].MainContract
).to.have.property("evm"); ).to.have.property('evm')
expect( expect(
output.contracts["fixtures/instantiate.sol"].MainContract.evm, output.contracts['fixtures/instantiate.sol'].MainContract.evm
).to.have.property("bytecode"); ).to.have.property('bytecode')
}); })
}); })
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"name": "@parity/resolc", "name": "@parity/resolc",
"license": "Apache-2.0", "license": "Apache-2.0",
"version": "0.0.0-updated-via-gh-releases", "version": "0.1.0-dev.16",
"author": "Parity <admin@parity.io> (https://parity.io)", "author": "Parity <admin@parity.io> (https://parity.io)",
"module": "index.ts", "module": "index.ts",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@@ -32,6 +32,6 @@
"@types/node": "^22.9.0", "@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.0 <=0.8.30"
} }
} }
+169 -172
View File
@@ -8,196 +8,193 @@ import * as resolc from '.'
import { SolcInput } from '.' import { SolcInput } from '.'
async function main() { async function main() {
// hold on to any exception handlers that existed prior to this script running, we'll be adding them back at the end // hold on to any exception handlers that existed prior to this script running, we'll be adding them back at the end
const originalUncaughtExceptionListeners = const originalUncaughtExceptionListeners =
process.listeners('uncaughtException') process.listeners('uncaughtException')
// FIXME: remove annoying exception catcher of Emscripten // FIXME: remove annoying exception catcher of Emscripten
// see https://github.com/chriseth/browser-solidity/issues/167 // see https://github.com/chriseth/browser-solidity/issues/167
process.removeAllListeners('uncaughtException') process.removeAllListeners('uncaughtException')
const program = new commander.Command() const program = new commander.Command()
program.name('solcjs') program.name('solcjs')
program.version(resolc.version()) program.version(resolc.version())
program program
.option('--bin', 'Binary of the contracts in hex.') .option('--bin', 'Binary of the contracts in hex.')
.option('--abi', 'ABI of the contracts.') .option('--abi', 'ABI of the contracts.')
.option( .option(
'--base-path <path>', '--base-path <path>',
'Root of the project source tree. ' + 'Root of the project source tree. ' +
'The import callback will attempt to interpret all import paths as relative to this directory.' 'The import callback will attempt to interpret all import paths as relative to this directory.'
) )
.option( .option(
'--include-path <path...>', '--include-path <path...>',
'Extra source directories available to the import callback. ' + 'Extra source directories available to the import callback. ' +
'When using a package manager to install libraries, use this option to specify directories where packages are installed. ' + 'When using a package manager to install libraries, use this option to specify directories where packages are installed. ' +
'Can be used multiple times to provide multiple locations.' 'Can be used multiple times to provide multiple locations.'
) )
.option( .option(
'-o, --output-dir <output-directory>', '-o, --output-dir <output-directory>',
'Output directory for the contracts.' 'Output directory for the contracts.'
) )
.option('-p, --pretty-json', 'Pretty-print all JSON output.', false) .option('-p, --pretty-json', 'Pretty-print all JSON output.', false)
.option('-v, --verbose', 'More detailed console output.', false) .option('-v, --verbose', 'More detailed console output.', false)
.argument('<files...>') .argument('<files...>')
program.parse(process.argv) program.parse(process.argv)
const options = program.opts<{ const options = program.opts<{
verbose: boolean verbose: boolean
abi: boolean abi: boolean
bin: boolean bin: boolean
outputDir?: string outputDir?: string
prettyJson: boolean prettyJson: boolean
basePath?: string basePath?: string
includePath?: string[] includePath?: string[]
}>() }>()
const files: string[] = program.args const files: string[] = program.args
const destination = options.outputDir ?? '.' const destination = options.outputDir ?? '.'
function abort(msg: string) { function abort(msg: string) {
console.error(msg || 'Error occurred') console.error(msg || 'Error occurred')
process.exit(1) process.exit(1)
}
function withUnixPathSeparators(filePath: string) {
// On UNIX-like systems forward slashes in paths are just a part of the file name.
if (os.platform() !== 'win32') {
return filePath
} }
function withUnixPathSeparators(filePath: string) { return filePath.replace(/\\/g, '/')
// On UNIX-like systems forward slashes in paths are just a part of the file name. }
if (os.platform() !== 'win32') {
return filePath
}
return filePath.replace(/\\/g, '/') function makeSourcePathRelativeIfPossible(sourcePath: string) {
} const absoluteBasePath = options.basePath
? path.resolve(options.basePath)
function makeSourcePathRelativeIfPossible(sourcePath: string) { : path.resolve('.')
const absoluteBasePath = options.basePath const absoluteIncludePaths = options.includePath
? path.resolve(options.basePath) ? options.includePath.map((prefix: string) => {
: path.resolve('.') return path.resolve(prefix)
const absoluteIncludePaths = options.includePath
? options.includePath.map((prefix: string) => {
return path.resolve(prefix)
})
: []
// Compared to base path stripping logic in solc this is much simpler because path.resolve()
// handles symlinks correctly (does not resolve them except in work dir) and strips .. segments
// from paths going beyond root (e.g. `/../../a/b/c` -> `/a/b/c/`). It's simpler also because it
// ignores less important corner cases: drive letters are not stripped from absolute paths on
// Windows and UNC paths are not handled in a special way (at least on Linux). Finally, it has
// very little test coverage so there might be more differences that we are just not aware of.
const absoluteSourcePath = path.resolve(sourcePath)
for (const absolutePrefix of [absoluteBasePath].concat(
absoluteIncludePaths
)) {
const relativeSourcePath = path.relative(
absolutePrefix,
absoluteSourcePath
)
if (!relativeSourcePath.startsWith('../')) {
return withUnixPathSeparators(relativeSourcePath)
}
}
// File is not located inside base path or include paths so use its absolute path.
return withUnixPathSeparators(absoluteSourcePath)
}
function toFormattedJson<T>(input: T) {
return JSON.stringify(input, null, options.prettyJson ? 4 : 0)
}
if (files.length === 0) {
console.error('Must provide a file')
process.exit(1)
}
if (!(options.bin || options.abi)) {
abort('Invalid option selected, must specify either --bin or --abi')
}
const sources: SolcInput = {}
for (let i = 0; i < files.length; i++) {
try {
sources[makeSourcePathRelativeIfPossible(files[i])] = {
content: fs.readFileSync(files[i]).toString(),
}
} catch (e) {
abort('Error reading ' + files[i] + ': ' + e)
}
}
if (options.verbose) {
console.log('>>> Compiling:\n' + toFormattedJson(sources) + '\n')
}
const output = await resolc.compile(sources)
let hasError = false
if (!output) {
abort('No output from compiler')
} else if (output.errors) {
for (const error in output.errors) {
const message = output.errors[error]
if (message.severity === 'warning') {
console.log(message.formattedMessage)
} else {
console.error(message.formattedMessage)
hasError = true
}
}
}
fs.mkdirSync(destination, { recursive: true })
function writeFile(file: string, content: Buffer | string) {
file = path.join(destination, file)
fs.writeFile(file, content, function (err) {
if (err) {
console.error('Failed to write ' + file + ': ' + err)
}
}) })
: []
// Compared to base path stripping logic in solc this is much simpler because path.resolve()
// handles symlinks correctly (does not resolve them except in work dir) and strips .. segments
// from paths going beyond root (e.g. `/../../a/b/c` -> `/a/b/c/`). It's simpler also because it
// ignores less important corner cases: drive letters are not stripped from absolute paths on
// Windows and UNC paths are not handled in a special way (at least on Linux). Finally, it has
// very little test coverage so there might be more differences that we are just not aware of.
const absoluteSourcePath = path.resolve(sourcePath)
for (const absolutePrefix of [absoluteBasePath].concat(
absoluteIncludePaths
)) {
const relativeSourcePath = path.relative(
absolutePrefix,
absoluteSourcePath
)
if (!relativeSourcePath.startsWith('../')) {
return withUnixPathSeparators(relativeSourcePath)
}
} }
for (const fileName in output.contracts) { // File is not located inside base path or include paths so use its absolute path.
for (const contractName in output.contracts[fileName]) { return withUnixPathSeparators(absoluteSourcePath)
let contractFileName = fileName + ':' + contractName }
contractFileName = contractFileName.replace(/[:./\\]/g, '_')
if (options.bin) { function toFormattedJson<T>(input: T) {
writeFile( return JSON.stringify(input, null, options.prettyJson ? 4 : 0)
contractFileName + '.polkavm', }
Buffer.from(
output.contracts[fileName][contractName].evm.bytecode
.object,
'hex'
)
)
}
if (options.abi) { if (files.length === 0) {
writeFile( console.error('Must provide a file')
contractFileName + '.abi', process.exit(1)
toFormattedJson( }
output.contracts[fileName][contractName].abi
) if (!(options.bin || options.abi)) {
) abort('Invalid option selected, must specify either --bin or --abi')
} }
}
const sources: SolcInput = {}
for (let i = 0; i < files.length; i++) {
try {
sources[makeSourcePathRelativeIfPossible(files[i])] = {
content: fs.readFileSync(files[i]).toString(),
}
} catch (e) {
abort('Error reading ' + files[i] + ': ' + e)
} }
}
// Put back original exception handlers. if (options.verbose) {
originalUncaughtExceptionListeners.forEach(function (listener) { console.log('>>> Compiling:\n' + toFormattedJson(sources) + '\n')
process.addListener('uncaughtException', listener) }
const output = await resolc.compile(sources)
let hasError = false
if (!output) {
abort('No output from compiler')
} else if (output.errors) {
for (const error in output.errors) {
const message = output.errors[error]
if (message.severity === 'warning') {
console.log(message.formattedMessage)
} else {
console.error(message.formattedMessage)
hasError = true
}
}
}
fs.mkdirSync(destination, { recursive: true })
function writeFile(file: string, content: Buffer | string) {
file = path.join(destination, file)
fs.writeFile(file, content, function (err) {
if (err) {
console.error('Failed to write ' + file + ': ' + err)
}
}) })
}
if (hasError) { for (const fileName in output.contracts) {
process.exit(1) for (const contractName in output.contracts[fileName]) {
let contractFileName = fileName + ':' + contractName
contractFileName = contractFileName.replace(/[:./\\]/g, '_')
if (options.bin) {
writeFile(
contractFileName + '.polkavm',
Buffer.from(
output.contracts[fileName][contractName].evm.bytecode.object,
'hex'
)
)
}
if (options.abi) {
writeFile(
contractFileName + '.abi',
toFormattedJson(output.contracts[fileName][contractName].abi)
)
}
} }
}
// Put back original exception handlers.
originalUncaughtExceptionListeners.forEach(function (listener) {
process.addListener('uncaughtException', listener)
})
if (hasError) {
process.exit(1)
}
} }
main().catch((err) => { main().catch((err) => {
console.error('Error:', err) console.error('Error:', err)
process.exit(1) process.exit(1)
}) })
+88 -87
View File
@@ -4,112 +4,113 @@ import assert from 'node:assert'
import { compile, tryResolveImport } from '.' import { compile, tryResolveImport } from '.'
import { resolve } from 'node:path' import { resolve } from 'node:path'
const compileOptions = [{}] const compileOptions = [{}]
if (existsSync('../../target/release/resolc')) { if (existsSync('../../target/release/resolc')) {
compileOptions.push({ bin: '../../target/release/resolc' }) compileOptions.push({ bin: '../../target/release/resolc' })
} }
for (const options of compileOptions) { for (const options of compileOptions) {
test(`check Ok output with option ${JSON.stringify(options)}`, async () => { test(`check Ok output with option ${JSON.stringify(options)}`, async () => {
const contract = 'fixtures/token.sol' const contract = 'fixtures/token.sol'
const sources = { const sources = {
[contract]: { [contract]: {
content: readFileSync('fixtures/storage.sol', 'utf8'), content: readFileSync('fixtures/storage.sol', 'utf8'),
}, },
} }
const out = await compile(sources, options) const out = await compile(sources, options)
assert(out.contracts[contract].Storage.abi != null) assert(out.contracts[contract].Storage.abi != null)
assert(out.contracts[contract].Storage.evm.bytecode != null) assert(out.contracts[contract].Storage.evm.bytecode != null)
}) })
} }
test('check Err output', async () => { test('check Err output', async () => {
const sources = { const sources = {
bad: { bad: {
content: readFileSync('fixtures/storage_bad.sol', 'utf8'), content: readFileSync('fixtures/storage_bad.sol', 'utf8'),
}, },
} }
const out = await compile(sources) const out = await compile(sources)
assert( assert(
out?.errors?.[0].message.includes( out?.errors?.[0].message.includes(
'SPDX license identifier not provided in source file' 'SPDX license identifier not provided in source file'
)
) )
assert( )
out?.errors?.[1].message.includes( assert(
'Source file does not specify required compiler version' out?.errors?.[1].message.includes(
) 'Source file does not specify required compiler version'
) )
)
}) })
test('check Err from stderr', async () => { test('check Err from stderr', async () => {
const sources = { const sources = {
bad: { bad: {
content: readFileSync('fixtures/bad_pragma.sol', 'utf8'), content: readFileSync('fixtures/bad_pragma.sol', 'utf8'),
}, },
} }
try { try {
await compile(sources) await compile(sources)
assert(false, 'Expected error') assert(false, 'Expected error')
} catch (error) { } catch (error) {
assert( assert(
String(error).includes( String(error).includes('Source file requires different compiler version')
'Source file requires different compiler version' )
) }
)
}
}) })
test('resolve import', () => { test('resolve import', () => {
const cases = [ const cases = [
// local // local
{ {
file: './fixtures/storage.sol', file: './fixtures/storage.sol',
expected: resolve('fixtures/storage.sol'), expected: resolve('fixtures/storage.sol'),
}, },
// 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('@openzeppelin/contracts/token/ERC20/ERC20.sol'), expected: require.resolve(
}, '@openzeppelin/contracts/token/ERC20/ERC20.sol'
// scopped module without version ),
{ },
file: '@openzeppelin/contracts/token/ERC20/ERC20.sol', // scopped module without version
expected: require.resolve('@openzeppelin/contracts/token/ERC20/ERC20.sol'), {
}, file: '@openzeppelin/contracts/token/ERC20/ERC20.sol',
// scopped module with wrong version expected: require.resolve(
{ '@openzeppelin/contracts/token/ERC20/ERC20.sol'
file: '@openzeppelin/contracts@4.8.3/token/ERC20/ERC20.sol', ),
expected: `Error: Version mismatch: Specified @openzeppelin/contracts@4.8.3, but installed version is 5.1.0`, },
}, // scopped module with wrong version
// module without version {
{ file: '@openzeppelin/contracts@4.8.3/token/ERC20/ERC20.sol',
file: '@openzeppelin/contracts/package.json', expected: `Error: Version mismatch: Specified @openzeppelin/contracts@4.8.3, but installed version is 5.1.0`,
expected: require.resolve('@openzeppelin/contracts/package.json'), },
}, // module without version
// scopped module with version {
{ file: '@openzeppelin/contracts/package.json',
file: '@openzeppelin/contracts@5.1.0/package.json', expected: require.resolve('@openzeppelin/contracts/package.json'),
expected: require.resolve('@openzeppelin/contracts/package.json'), },
}, // scopped module with version
] {
file: '@openzeppelin/contracts@5.1.0/package.json',
expected: require.resolve('@openzeppelin/contracts/package.json'),
},
]
for (const { file, expected } of cases) { for (const { file, expected } of cases) {
try { try {
const resolved = tryResolveImport(file) const resolved = tryResolveImport(file)
assert( assert(
resolved === expected, resolved === expected,
`\nGot:\n${resolved}\nExpected:\n${expected}` `\nGot:\n${resolved}\nExpected:\n${expected}`
) )
} catch (error) { } catch (error) {
assert( assert(
String(error) == expected, String(error) == expected,
`\nGot:\n${String(error)}\nExpected:\n${expected}` `\nGot:\n${String(error)}\nExpected:\n${expected}`
) )
}
} }
}
}) })
+164 -153
View File
@@ -5,114 +5,127 @@ import path from 'path'
import { existsSync, readFileSync } from 'fs' import { existsSync, readFileSync } from 'fs'
export type SolcInput = { export type SolcInput = {
[contractName: string]: { [contractName: string]: {
content: string content: string
} }
} }
export type SolcError = { export type SolcError = {
component: string component: string
errorCode: string errorCode: string
formattedMessage: string formattedMessage: string
message: string message: string
severity: string severity: string
sourceLocation?: { sourceLocation?: {
file: string file: string
start: number start: number
end: number end: number
} }
type: string type: string
} }
export type SolcOutput = { export type SolcOutput = {
contracts: { contracts: {
[contractPath: string]: { [contractPath: string]: {
[contractName: string]: { [contractName: string]: {
abi: Array<{ abi: Array<{
name: string name: string
inputs: Array<{ name: string; type: string }> inputs: Array<{ name: string; type: string }>
outputs: Array<{ name: string; type: string }> outputs: Array<{ name: string; type: string }>
stateMutability: string stateMutability: string
type: string type: string
}> }>
evm: { evm: {
bytecode: { object: string } bytecode: { object: string }
}
}
} }
}
} }
errors?: Array<SolcError> }
errors?: Array<SolcError>
} }
export function resolveInputs(sources: SolcInput): SolcInput { export function resolveInputs(sources: SolcInput): SolcInput {
const input = { const input = {
language: 'Solidity', language: 'Solidity',
sources, sources,
settings: { settings: {
outputSelection: { outputSelection: {
'*': { '*': {
'*': ['evm.bytecode.object'], '*': ['evm.bytecode.object'],
},
},
}, },
} },
},
}
const out = solc.compile(JSON.stringify(input), { const out = solc.compile(JSON.stringify(input), {
import: (path: string) => { import: (path: string) => {
return { return {
contents: readFileSync(tryResolveImport(path), 'utf8'), contents: readFileSync(tryResolveImport(path), 'utf8'),
} }
},
})
const output = JSON.parse(out) as {
sources: { [fileName: string]: { id: number } }
errors: Array<SolcError>
}
if (output.errors && Object.keys(output.sources).length === 0) {
throw new Error(output.errors[0].formattedMessage)
}
return Object.fromEntries(
Object.keys(output.sources).map((fileName) => {
return [
fileName,
sources[fileName] ?? {
content: readFileSync(tryResolveImport(fileName), 'utf8'),
}, },
]
}) })
)
const output = JSON.parse(out) as {
sources: { [fileName: string]: { id: number } }
errors: Array<SolcError>
}
if (output.errors && Object.keys(output.sources).length === 0) {
throw new Error(output.errors[0].formattedMessage)
}
return Object.fromEntries(
Object.keys(output.sources).map((fileName) => {
return [
fileName,
sources[fileName] ?? {
content: readFileSync(tryResolveImport(fileName), 'utf8'),
},
]
})
)
} }
export function version(): string { export function version(): string {
const v = resolcVersion() const v = resolcVersion()
return v.split(' ').pop() ?? v return v.split(' ').pop() ?? v
} }
export async function compile( export async function compile(
sources: SolcInput, sources: SolcInput,
option: { bin?: string } = {} option: {
optimizer?: Record<string, unknown>
bin?: string
} = {}
): Promise<SolcOutput> { ): Promise<SolcOutput> {
const input = JSON.stringify({ const {
language: 'Solidity', optimizer = {
sources: resolveInputs(sources), mode: 'z',
settings: { fallback_to_optimizing_for_size: true,
optimizer: { enabled: true, runs: 200 }, enabled: true,
outputSelection: { runs: 200,
'*': { },
'*': ['abi'], bin,
}, } = option
},
const input = JSON.stringify({
language: 'Solidity',
sources: resolveInputs(sources),
settings: {
optimizer,
outputSelection: {
'*': {
'*': ['abi'],
}, },
}) },
},
})
if (option.bin) { if (bin) {
return compileWithBin(input, option.bin) return compileWithBin(input, bin)
} }
return resolc(input) return resolc(input)
} }
/** /**
@@ -120,83 +133,81 @@ export async function compile(
* @param importPath - The import path to resolve. * @param importPath - The import path to resolve.
*/ */
export function tryResolveImport(importPath: string) { export function tryResolveImport(importPath: string) {
// resolve local path // resolve local path
if (existsSync(importPath)) { if (existsSync(importPath)) {
return path.resolve(importPath) return path.resolve(importPath)
}
const importRegex = /^(@?[^@/]+(?:\/[^@/]+)?)(?:@([^/]+))?(\/.+)$/
const match = importPath.match(importRegex)
if (!match) {
throw new Error('Invalid import path format.')
}
const basePackage = match[1] // "foo", "@scope/foo"
const specifiedVersion = match[2] // "1.2.3" (optional)
const relativePath = match[3] // "/path/to/file.sol"
let packageJsonPath
try {
packageJsonPath = require.resolve(path.join(basePackage, 'package.json'))
} catch {
throw new Error(`Could not resolve package ${basePackage}`)
}
// Check if a version was specified and compare with the installed version
if (specifiedVersion) {
const installedVersion = JSON.parse(
readFileSync(packageJsonPath, 'utf-8')
).version
if (installedVersion !== specifiedVersion) {
throw new Error(
`Version mismatch: Specified ${basePackage}@${specifiedVersion}, but installed version is ${installedVersion}`
)
} }
}
const importRegex = /^(@?[^@/]+(?:\/[^@/]+)?)(?:@([^/]+))?(\/.+)$/ const packageRoot = path.dirname(packageJsonPath)
const match = importPath.match(importRegex)
if (!match) { // Construct full path to the requested file
throw new Error('Invalid import path format.') const resolvedPath = path.join(packageRoot, relativePath)
} if (existsSync(resolvedPath)) {
return resolvedPath
const basePackage = match[1] // "foo", "@scope/foo" } else {
const specifiedVersion = match[2] // "1.2.3" (optional) throw new Error(`Resolved path ${resolvedPath} does not exist.`)
const relativePath = match[3] // "/path/to/file.sol" }
let packageJsonPath
try {
packageJsonPath = require.resolve(
path.join(basePackage, 'package.json')
)
} catch {
throw new Error(`Could not resolve package ${basePackage}`)
}
// Check if a version was specified and compare with the installed version
if (specifiedVersion) {
const installedVersion = JSON.parse(
readFileSync(packageJsonPath, 'utf-8')
).version
if (installedVersion !== specifiedVersion) {
throw new Error(
`Version mismatch: Specified ${basePackage}@${specifiedVersion}, but installed version is ${installedVersion}`
)
}
}
const packageRoot = path.dirname(packageJsonPath)
// Construct full path to the requested file
const resolvedPath = path.join(packageRoot, relativePath)
if (existsSync(resolvedPath)) {
return resolvedPath
} else {
throw new Error(`Resolved path ${resolvedPath} does not exist.`)
}
} }
function compileWithBin(input: string, bin: string): PromiseLike<SolcOutput> { function compileWithBin(input: string, bin: string): PromiseLike<SolcOutput> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const process = spawn(bin, ['--standard-json']) const process = spawn(bin, ['--standard-json'])
let output = '' let output = ''
let error = '' let error = ''
process.stdin.write(input) process.stdin.write(input)
process.stdin.end() process.stdin.end()
process.stdout.on('data', (data) => { process.stdout.on('data', (data) => {
output += data.toString() output += data.toString()
})
process.stderr.on('data', (data) => {
error += data.toString()
})
process.on('close', (code) => {
if (code === 0) {
try {
const result: SolcOutput = JSON.parse(output)
resolve(result)
} catch {
reject(new Error(`Failed to parse output`))
}
} else {
reject(new Error(`Process exited with code ${code}: ${error}`))
}
})
}) })
process.stderr.on('data', (data) => {
error += data.toString()
})
process.on('close', (code) => {
if (code === 0) {
try {
const result: SolcOutput = JSON.parse(output)
resolve(result)
} catch {
reject(new Error(`Failed to parse output`))
}
} else {
reject(new Error(`Process exited with code ${code}: ${error}`))
}
})
})
} }
+17 -17
View File
@@ -3,28 +3,28 @@ import Resolc from './resolc/resolc'
import type { SolcOutput } from '.' import type { SolcOutput } from '.'
export function resolc(input: string): SolcOutput { export function resolc(input: string): SolcOutput {
const m = Resolc() as any // eslint-disable-line @typescript-eslint/no-explicit-any const m = Resolc() as any // eslint-disable-line @typescript-eslint/no-explicit-any
m.soljson = soljson m.soljson = soljson
m.writeToStdin(input) m.writeToStdin(input)
m.callMain(['--standard-json']) m.callMain(['--standard-json'])
const err = m.readFromStderr() const err = m.readFromStderr()
if (err) { if (err) {
throw new Error(err) throw new Error(err)
} }
return JSON.parse(m.readFromStdout()) as SolcOutput return JSON.parse(m.readFromStdout()) as SolcOutput
} }
export function version(): string { export function version(): string {
const m = Resolc() as any // eslint-disable-line @typescript-eslint/no-explicit-any const m = Resolc() as any // eslint-disable-line @typescript-eslint/no-explicit-any
m.soljson = soljson m.soljson = soljson
m.callMain(['--version']) m.callMain(['--version'])
const err = m.readFromStderr() const err = m.readFromStderr()
if (err) { if (err) {
throw new Error(err) throw new Error(err)
} }
return m.readFromStdout() return m.readFromStdout()
} }
+82 -82
View File
@@ -1,95 +1,95 @@
declare module 'solc/soljson' {} declare module 'solc/soljson' {}
declare module 'solc' { declare module 'solc' {
// Basic types for input/output handling // Basic types for input/output handling
export interface CompileInput { export interface CompileInput {
language: string language: string
sources: { sources: {
[fileName: string]: { [fileName: string]: {
content: string content: string
} }
}
settings?: {
optimizer?: {
enabled: boolean
runs: number
}
outputSelection: {
[fileName: string]: {
[contractName: string]: string[]
} }
settings?: { }
optimizer?: { }
enabled: boolean }
runs: number
} export interface CompileOutput {
outputSelection: { errors?: Array<{
component: string
errorCode: string
formattedMessage: string
message: string
severity: string
sourceLocation?: {
file: string
start: number
end: number
}
type: string
}>
sources?: {
[fileName: string]: {
id: number
ast: object
}
}
contracts?: {
[fileName: string]: {
[contractName: string]: {
abi: object[]
evm: {
bytecode: {
object: string
sourceMap: string
linkReferences: {
[fileName: string]: { [fileName: string]: {
[contractName: string]: string[] [libraryName: string]: Array<{
start: number
length: number
}>
} }
}
} }
} deployedBytecode: {
} object: string
sourceMap: string
export interface CompileOutput { linkReferences: {
errors?: Array<{ [fileName: string]: {
component: string [libraryName: string]: Array<{
errorCode: string start: number
formattedMessage: string length: number
message: string }>
severity: string
sourceLocation?: {
file: string
start: number
end: number
}
type: string
}>
sources?: {
[fileName: string]: {
id: number
ast: object
}
}
contracts?: {
[fileName: string]: {
[contractName: string]: {
abi: object[]
evm: {
bytecode: {
object: string
sourceMap: string
linkReferences: {
[fileName: string]: {
[libraryName: string]: Array<{
start: number
length: number
}>
}
}
}
deployedBytecode: {
object: string
sourceMap: string
linkReferences: {
[fileName: string]: {
[libraryName: string]: Array<{
start: number
length: number
}>
}
}
}
}
} }
}
} }
}
} }
}
} }
}
// Main exported functions // Main exported functions
export function compile( export function compile(
input: string | CompileInput, input: string | CompileInput,
options?: { options?: {
import: (path: string) => import: (path: string) =>
| { | {
contents: string contents: string
error?: undefined error?: undefined
} }
| { | {
error: string error: string
contents?: undefined contents?: undefined
} }
} }
): string ): string
} }
+6618
View File
File diff suppressed because it is too large Load Diff