mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-14 21:31:05 +00:00
Compare commits
329 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f549a6b031 | |||
| be6f734cfc | |||
| 25ee4eef5a | |||
| 9446132608 | |||
| 91bd1b0b4e | |||
| e568a924ae | |||
| 11f82c8488 | |||
| d0c10e6d5c | |||
| 45ceab7dc7 | |||
| a9ccb1f9b4 | |||
| e7e40a0ded | |||
| 94b14b079b | |||
| 8cd10a6136 | |||
| 0742227c5a | |||
| 1e0cce0fa8 | |||
| b6a19c97b1 | |||
| d97d094a8a | |||
| 975b610d9f | |||
| ad61b6e3c9 | |||
| e78f3cc419 | |||
| a1090cfa5a | |||
| dd48baadc7 | |||
| a3a5c7380d | |||
| 79aaca1a85 | |||
| 8ee1860f12 | |||
| 878ca91689 | |||
| 046455db06 | |||
| 70037e1136 | |||
| c0cdde5a5a | |||
| df1921ba93 | |||
| 84018c18ae | |||
| 42cac55be8 | |||
| 6549a4f825 | |||
| f46bea6a96 | |||
| 2090830858 | |||
| 93c6387dae | |||
| 7346d82dfa | |||
| 39a6db7266 | |||
| 8240163be0 | |||
| b560d72139 | |||
| 6858cb9a61 | |||
| 1cc4f967b4 | |||
| 713b7e5409 | |||
| ffc13697ed | |||
| 94ec34c4d5 | |||
| 13faedf08a | |||
| 2f89c743ce | |||
| ea0ea711ad | |||
| b31e8a0d74 | |||
| 903cbd7159 | |||
| 72f9e4f66e | |||
| a96e1a4233 | |||
| 56aadce0a9 | |||
| 9751481f6b | |||
| c285a6ec3d | |||
| a0396dd6d0 | |||
| 141a8b752c | |||
| 7c932f719b | |||
| b238913a7d | |||
| ed608699af | |||
| 75fc23c810 | |||
| 486c9c28a1 | |||
| 7656c6a8b4 | |||
| 8754d802fa | |||
| 63f0266fff | |||
| e94432eaa0 | |||
| 1fc3aa1554 | |||
| a77ab501c8 | |||
| 8a3c587bbe | |||
| 45b6a57cae | |||
| 8a730f42cc | |||
| 413819facd | |||
| fa0ad68279 | |||
| 004c71d5d5 | |||
| 4d659ac2a6 | |||
| ed9dc60417 | |||
| 3b9144ef3b | |||
| bd4e108bb0 | |||
| 090e3ac13c | |||
| 3389865af7 | |||
| af39d506d9 | |||
| bb2f829361 | |||
| 1b8fcc4649 | |||
| 0e9e405f21 | |||
| 722dd86c27 | |||
| 0421869e4b | |||
| 32f55b976c | |||
| 459a786299 | |||
| fbaa45f283 | |||
| f2fac85dae | |||
| b8f3073e29 | |||
| 11d47d74ac | |||
| e3a9c95d32 | |||
| a560b2d919 | |||
| e07d0f0cb7 | |||
| f6a412eef4 | |||
| 20e77cb0b5 | |||
| 357bf58868 | |||
| f937188991 | |||
| 6e44488b4f | |||
| 5003f3e9ac | |||
| 431b5a2ce5 | |||
| ad3315346c | |||
| 516f79ee0f | |||
| 9f5443b6d6 | |||
| 0dafc779ce | |||
| 7d8fa75a0f | |||
| 80f94b5c76 | |||
| dab29bc89b | |||
| 313c033261 | |||
| 87c1d7a8be | |||
| 8a98346a9c | |||
| 1b4d2c6bb8 | |||
| 004b8ac16c | |||
| 497dae2494 | |||
| 36ea69b50f | |||
| 2bbc5d713d | |||
| 4a9b651235 | |||
| 66f9a4d64f | |||
| 76f4cf71d6 | |||
| 77e0344d80 | |||
| 2fb8beee62 | |||
| 93788e72e9 | |||
| 296a226d0b | |||
| 84deb3a29d | |||
| ee064671e0 | |||
| 63dfd046e5 | |||
| 08f341ccc1 | |||
| a07968205b | |||
| 7ffe64ed7c | |||
| f1bce4fe3f | |||
| 9efbb4c0b4 | |||
| bcc8fa892c | |||
| 89ec25da65 | |||
| 75a83af4da | |||
| 9c330ef8fc | |||
| 687cec31ef | |||
| 48a019e0ad | |||
| 17a2d2f9f2 | |||
| 6ad7908c5e | |||
| 840a736fc5 | |||
| 89cdfefab4 | |||
| 6c2c633651 | |||
| a73b0925c6 | |||
| ee650cf03a | |||
| c2210442b6 | |||
| cb268850a9 | |||
| f3a86588f3 | |||
| 7233738f45 | |||
| 79ec4dd04b | |||
| 374563bbe5 | |||
| a921e425b4 | |||
| 60fc09f787 | |||
| 10b8ff989c | |||
| 157647d5c1 | |||
| 35419e8202 | |||
| de3e7bf253 | |||
| 9fb24b607d | |||
| ba7310fdff | |||
| 4ef495beea | |||
| 8ed689e7ec | |||
| bfda465c32 | |||
| ab90af49df | |||
| 8201401fef | |||
| 1a8a7926e9 | |||
| bec5d60b7c | |||
| 3608a5a143 | |||
| 888723eb0d | |||
| fe1b3258d2 | |||
| d8a72e580b | |||
| bd1a22a702 | |||
| cec283986f | |||
| 06f43083c3 | |||
| b78b2b2af9 | |||
| 8ffe072fee | |||
| 71fb3ab8d6 | |||
| 3e7579580b | |||
| ad805543b3 | |||
| 4fbfb97b9e | |||
| 939138d0cd | |||
| 7f81f37e0c | |||
| fde9edab10 | |||
| d7d60da6f1 | |||
| f49d145e9a | |||
| 952c5cc894 | |||
| d8752ec6b5 | |||
| 6ad846a285 | |||
| 3f9771f838 | |||
| 22070a824d | |||
| d299dd1a19 | |||
| 14a598e840 | |||
| 55ec0988e8 | |||
| 909de515c4 | |||
| afe44ad21b | |||
| 2cb8f82266 | |||
| 6f2f158ef1 | |||
| 4e5482bad2 | |||
| 0110258d81 | |||
| f0d9d44dce | |||
| 3f6cd115ee | |||
| 7c00bbb0fc | |||
| 423a494621 | |||
| 08112e3449 | |||
| db6ca1fcfa | |||
| 54d154a73c | |||
| 93f1deb6a5 | |||
| 9a150b13f3 | |||
| 677cef9c0f | |||
| d25be523c1 | |||
| 6a4fd1e991 | |||
| 81915ddbcb | |||
| 0f25bac4bd | |||
| 7d41495587 | |||
| d6d5acfcce | |||
| 130ac48bf0 | |||
| b65fa2e42c | |||
| 1eb1083d40 | |||
| 4c0c74f7f4 | |||
| 229c0d452d | |||
| 4e024655a1 | |||
| 01b5ed5ba3 | |||
| 892c9b5fbe | |||
| 84d47fa738 | |||
| dbb47fd13e | |||
| 87f2bcefb3 | |||
| 05925f25f1 | |||
| 8990b2a486 | |||
| 89ddfb28c8 | |||
| 32d4b2309c | |||
| a772ff1354 | |||
| 0a82addf02 | |||
| 491850760f | |||
| fcbe00f4a9 | |||
| 77406264ea | |||
| cdebf69fc0 | |||
| f36d62ca73 | |||
| 39e504703e | |||
| 63da7212a1 | |||
| 3072c03600 | |||
| d88ddb25bd | |||
| 87dd77b784 | |||
| cece20deb1 | |||
| f57ab96eed | |||
| 3232382d96 | |||
| 6a120463c2 | |||
| 881b88354c | |||
| c9dd347755 | |||
| 140545ea15 | |||
| 010a2ed223 | |||
| 563864dd25 | |||
| ce8bf3d9ef | |||
| f947984671 | |||
| 14991f40ac | |||
| 6d16790f83 | |||
| 64fefe76b5 | |||
| f59b47df7b | |||
| 6e6fe20c71 | |||
| 93d2f3b9d9 | |||
| 8a225871ee | |||
| 677aedc6f3 | |||
| 90423ffb6b | |||
| 255176978a | |||
| 9a8003afbf | |||
| 7f3d0cecb2 | |||
| 18376432f1 | |||
| 007b79ee62 | |||
| 8c7d18aec7 | |||
| c0a82ce6d2 | |||
| c51d50bc88 | |||
| 94445bab93 | |||
| a934ec204e | |||
| b6baf6cfd9 | |||
| b7b28efded | |||
| d260472330 | |||
| 4f6debcbe3 | |||
| 9b23e19479 | |||
| 88a888d138 | |||
| 4cce4a729e | |||
| 9267a2af02 | |||
| 173ace72cb | |||
| 37ab2b6782 | |||
| 43d2ef3ce9 | |||
| 68564f9866 | |||
| 36d9317831 | |||
| 5b3b90db83 | |||
| a4043ecde7 | |||
| f985f42370 | |||
| aae25107a2 | |||
| cc38c37481 | |||
| e56feb95be | |||
| 995b115c5f | |||
| ee83d28a51 | |||
| 82ae22c163 | |||
| 20e3560398 | |||
| 5bf31db660 | |||
| f0f344a139 | |||
| 92a18460e4 | |||
| e5233fc46e | |||
| adda5a0f12 | |||
| d9842b5427 | |||
| 6335c34a2b | |||
| 49d2157a84 | |||
| e131eebf50 | |||
| 705d1d7866 | |||
| d5d419cefc | |||
| 8b7fe8e3d7 | |||
| b344e0cff5 | |||
| 0e6a6d12c3 | |||
| 77fe683f18 | |||
| e8fcd6118e | |||
| ad46e94ebd | |||
| 6585973e99 | |||
| 066acc4663 | |||
| 287272b789 | |||
| f621971a13 | |||
| b8bd44b62a | |||
| d8c708eba5 | |||
| 72d958392b | |||
| 616f044633 | |||
| 7dc8e6051e | |||
| 783fa640e4 | |||
| f086c57442 | |||
| 0f2b55f6ff | |||
| ac9215329c | |||
| 6635a0b337 | |||
| 77df88b3cb | |||
| 2955f77772 | |||
| d77ee1e0d4 | |||
| 5ac67bdc0d |
@@ -0,0 +1,20 @@
|
|||||||
|
[target.wasm32-unknown-emscripten]
|
||||||
|
rustflags = [
|
||||||
|
"-Clink-arg=-sEXPORTED_FUNCTIONS=_main,_free,_malloc",
|
||||||
|
"-Clink-arg=-sNO_INVOKE_RUN=1",
|
||||||
|
"-Clink-arg=-sEXIT_RUNTIME=1",
|
||||||
|
"-Clink-arg=-sALLOW_MEMORY_GROWTH=1",
|
||||||
|
"-Clink-arg=-sEXPORTED_RUNTIME_METHODS=FS,callMain,stringToNewUTF8",
|
||||||
|
"-Clink-arg=-sMODULARIZE=1",
|
||||||
|
"-Clink-arg=-sEXPORT_NAME=createRevive",
|
||||||
|
"-Clink-arg=-sWASM_ASYNC_COMPILATION=0",
|
||||||
|
"-Clink-arg=-sDYNAMIC_EXECUTION=0",
|
||||||
|
"-Clink-arg=-sALLOW_TABLE_GROWTH=1",
|
||||||
|
"-Clink-arg=--js-library=js/emscripten/embed/soljson_interface.js",
|
||||||
|
"-Clink-arg=--pre-js=js/emscripten/embed/pre.js",
|
||||||
|
"-Clink-arg=-sSTACK_SIZE=128kb",
|
||||||
|
"-Clink-arg=-sNODEJS_CATCH_EXIT=0"
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
rustdocflags = ["-D", "warnings"]
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
emsdk
|
||||||
|
llvm*/
|
||||||
|
target-llvm/
|
||||||
|
target/
|
||||||
|
node_modules/
|
||||||
|
utils/
|
||||||
|
build/
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
name: "Get Emscripten SDK"
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: ""
|
||||||
|
required: false
|
||||||
|
default: "4.0.9"
|
||||||
|
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: install emsdk
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
git clone https://github.com/emscripten-core/emsdk.git ./emsdk/
|
||||||
|
cd emsdk
|
||||||
|
git checkout tags/${{ inputs.version }}
|
||||||
|
./emsdk install ${{ inputs.version }}
|
||||||
|
./emsdk activate ${{ inputs.version }}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
# example:
|
||||||
|
# - uses: ./.github/actions/get-llvm
|
||||||
|
# with:
|
||||||
|
# target: x86_64-unknown-linux-gnu
|
||||||
|
|
||||||
|
name: "Download LLVM"
|
||||||
|
inputs:
|
||||||
|
target:
|
||||||
|
required: true
|
||||||
|
version:
|
||||||
|
description: "LLVM release version (e.g., 'llvm-18.1.8'). If not specified, uses the latest LLVM release."
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: detect llvm version from submodule
|
||||||
|
id: detect-version
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
if [ -z "${{ inputs.version }}" ]; then
|
||||||
|
# Get the full version from the LLVM submodule.
|
||||||
|
git submodule update --init --recursive
|
||||||
|
cd llvm
|
||||||
|
# Try to get the most recent tag (e.g., "llvmorg-18.1.8")
|
||||||
|
LLVM_TAG=$(git describe --tags --abbrev=0 2>/dev/null)
|
||||||
|
if [ -n "$LLVM_TAG" ]; then
|
||||||
|
echo "Detected LLVM version from submodule: $LLVM_TAG"
|
||||||
|
# Convert "llvmorg-x.y.z" to "llvm-x.y.z"
|
||||||
|
LLVM_VERSION=$(echo "$LLVM_TAG" | sed 's/^llvmorg-/llvm-/')
|
||||||
|
echo "Detected LLVM version from submodule: $LLVM_VERSION"
|
||||||
|
echo "version_prefix=$LLVM_VERSION" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "Could not detect LLVM version from submodule, will use latest release"
|
||||||
|
echo "version_prefix=" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
cd ..
|
||||||
|
else
|
||||||
|
echo "Using explicitly provided version: ${{ inputs.version }}"
|
||||||
|
echo "version_prefix=${{ inputs.version }}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: find asset
|
||||||
|
id: find
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
env:
|
||||||
|
target: ${{ inputs.target }}
|
||||||
|
version_prefix: ${{ steps.detect-version.outputs.version_prefix }}
|
||||||
|
with:
|
||||||
|
result-encoding: string
|
||||||
|
script: |
|
||||||
|
let page = 1;
|
||||||
|
let releases = [];
|
||||||
|
|
||||||
|
let target = process.env.target
|
||||||
|
let versionPrefix = process.env.version_prefix
|
||||||
|
|
||||||
|
do {
|
||||||
|
const res = await github.rest.repos.listReleases({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
per_page: 50,
|
||||||
|
page,
|
||||||
|
});
|
||||||
|
|
||||||
|
releases = res.data
|
||||||
|
releases.sort((a, b) => {
|
||||||
|
return (a.published_at < b.published_at) ? 1 : ((a.published_at > b.published_at) ? -1 : 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
let llvmRelease;
|
||||||
|
if (versionPrefix) {
|
||||||
|
// Search for latest release matching the version prefix
|
||||||
|
llvmRelease = releases.find(release => {
|
||||||
|
return release.tag_name.startsWith(versionPrefix);
|
||||||
|
});
|
||||||
|
if (llvmRelease) {
|
||||||
|
core.info(`Found LLVM release matching prefix '${versionPrefix}': ${llvmRelease.tag_name}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Find latest LLVM release
|
||||||
|
llvmRelease = releases.find(release => {
|
||||||
|
return release.tag_name.startsWith('llvm-');
|
||||||
|
});
|
||||||
|
if (llvmRelease) {
|
||||||
|
core.info(`Found latest LLVM version: ${llvmRelease.tag_name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (llvmRelease){
|
||||||
|
let asset = llvmRelease.assets.find(asset =>{
|
||||||
|
return asset.name.includes(target);
|
||||||
|
});
|
||||||
|
if (!asset){
|
||||||
|
core.setFailed(`Artifact for '${target}' not found in release ${llvmRelease.tag_name} (${llvmRelease.html_url})`);
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
return asset.browser_download_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
page++;
|
||||||
|
} while(releases.length > 0);
|
||||||
|
|
||||||
|
if (versionPrefix) {
|
||||||
|
core.setFailed(`No LLVM releases matching prefix '${versionPrefix}' found! Please check the version.`);
|
||||||
|
} else {
|
||||||
|
core.setFailed(`No LLVM releases found! Please release LLVM before running this workflow.`);
|
||||||
|
}
|
||||||
|
process.exit();
|
||||||
|
|
||||||
|
- name: download
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
curl -sSLo llvm.tar.gz ${{ steps.find.outputs.result }}
|
||||||
|
|
||||||
|
- name: unpack
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
tar -xf llvm.tar.gz
|
||||||
|
rm llvm.tar.gz
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
name: "Install Solidity Compiler"
|
||||||
|
description: "Installs the Ethereum solc Solidity compiler frontend executable"
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Figure out Solc Download URL
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
if [[ "${{ runner.os }}" == "Linux" ]]; then
|
||||||
|
echo "SOLC_NAME=solc-static-linux" >> $GITHUB_ENV
|
||||||
|
elif [[ "${{ runner.os }}" == "Windows" ]]; then
|
||||||
|
echo "SOLC_NAME=solc-windows.exe" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "SOLC_NAME=solc-macos" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Download Solc
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir -p solc
|
||||||
|
curl -sSL --output solc/solc https://github.com/ethereum/solidity/releases/download/v0.8.31/${SOLC_NAME}
|
||||||
|
|
||||||
|
- name: Make Solc Executable
|
||||||
|
if: ${{ runner.os == 'Windows' }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "$(pwd -W)\\solc" >> $GITHUB_PATH
|
||||||
|
mv solc/solc solc/solc.exe
|
||||||
|
|
||||||
|
- name: Make Solc Executable
|
||||||
|
if: ${{ runner.os != 'Windows' }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "$(pwd)/solc" >> $GITHUB_PATH
|
||||||
|
chmod +x solc/solc
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
This script generates JSON files for different platforms based on GitHub release data.
|
||||||
|
It fetches release information from a specified GitHub repository and tag,
|
||||||
|
parses the release assets, and generates JSON files for each platform with relevant metadata.
|
||||||
|
It also handles checksum files and updates a list.json file for each platform.
|
||||||
|
It requires the GITHUB_TOKEN environment variable to be set for authentication.
|
||||||
|
Usage:
|
||||||
|
python json_generator.py <repo> <tag>
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
def validate_github_token():
|
||||||
|
"""Validate that GITHUB_TOKEN environment variable is set."""
|
||||||
|
if 'GITHUB_TOKEN' not in os.environ:
|
||||||
|
print("Error: GITHUB_TOKEN environment variable is not set.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def fetch_release_data(repo, tag):
|
||||||
|
"""Fetch release data from GitHub API."""
|
||||||
|
url = f"https://api.github.com/repos/{repo}/releases/tags/{tag}"
|
||||||
|
headers = {
|
||||||
|
'Authorization': f"Bearer {os.environ['GITHUB_TOKEN']}",
|
||||||
|
'Accept': 'application/vnd.github+json',
|
||||||
|
'X-GitHub-Api-Version': '2022-11-28'
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(url, headers=headers)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except requests.RequestException as e:
|
||||||
|
print(f"Error fetching release data: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def fetch_checksum_file(release_data):
|
||||||
|
"""
|
||||||
|
Fetch the checksum.txt file from the release assets
|
||||||
|
and parse it into a dictionary mapping file names to their SHA256 checksums.
|
||||||
|
"""
|
||||||
|
checksums = {}
|
||||||
|
|
||||||
|
# Find the checksum.txt asset URL
|
||||||
|
checksum_asset = None
|
||||||
|
for asset in release_data['assets']:
|
||||||
|
if asset['name'] == 'checksums.txt':
|
||||||
|
checksum_asset = asset
|
||||||
|
break
|
||||||
|
|
||||||
|
if not checksum_asset:
|
||||||
|
print("Warning: checksum.txt file not found in release assets.")
|
||||||
|
return checksums
|
||||||
|
|
||||||
|
# Download the checksum file
|
||||||
|
headers = {
|
||||||
|
'Authorization': f"Bearer {os.environ['GITHUB_TOKEN']}",
|
||||||
|
'Accept': 'application/octet-stream'
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(checksum_asset['browser_download_url'], headers=headers)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
# Parse checksum file
|
||||||
|
for line in response.text.splitlines():
|
||||||
|
if line.strip():
|
||||||
|
checksum, filename = line.strip().split(None, 1)
|
||||||
|
checksums[filename] = checksum
|
||||||
|
|
||||||
|
return checksums
|
||||||
|
except requests.RequestException as e:
|
||||||
|
print(f"Error fetching checksum file: {e}")
|
||||||
|
return checksums
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error parsing checksum file: {e}")
|
||||||
|
return checksums
|
||||||
|
|
||||||
|
def extract_build_hash(target_commitish):
|
||||||
|
"""Extract the first 8 characters of the commit hash."""
|
||||||
|
return f"commit.{target_commitish[:8]}"
|
||||||
|
|
||||||
|
def generate_asset_json(release_data, asset, checksums):
|
||||||
|
"""Generate JSON for a specific asset."""
|
||||||
|
version = release_data['tag_name'].lstrip('v')
|
||||||
|
build = extract_build_hash(release_data['target_commitish'])
|
||||||
|
long_version = f"{version}+{build}"
|
||||||
|
|
||||||
|
# Get SHA256 checksum if available
|
||||||
|
sha256 = checksums.get(asset['name'], "")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"name": asset['name'],
|
||||||
|
"version": version,
|
||||||
|
"build": build,
|
||||||
|
"longVersion": long_version,
|
||||||
|
"url": asset['browser_download_url'],
|
||||||
|
"sha256": sha256,
|
||||||
|
"firstSolcVersion": os.environ.get("FIRST_SOLC_VERSION", ""),
|
||||||
|
"lastSolcVersion": os.environ.get("LAST_SOLC_VERSION", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
def save_platform_json(platform_folder, asset_json, tag):
|
||||||
|
"""Save asset JSON and update list.json for a specific platform."""
|
||||||
|
# Create platform folder if it doesn't exist
|
||||||
|
os.makedirs(platform_folder, exist_ok=True)
|
||||||
|
|
||||||
|
# Update or create list.json
|
||||||
|
list_file_path = os.path.join(platform_folder, "list.json")
|
||||||
|
|
||||||
|
if os.path.exists(list_file_path):
|
||||||
|
with open(list_file_path, 'r') as f:
|
||||||
|
try:
|
||||||
|
list_data = json.load(f)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
list_data = {"builds": [], "releases": {}, "latestRelease": ""}
|
||||||
|
else:
|
||||||
|
list_data = {"builds": [], "releases": {}, "latestRelease": ""}
|
||||||
|
|
||||||
|
# Remove any existing entry with the same path
|
||||||
|
list_data['builds'] = [
|
||||||
|
build for build in list_data['builds']
|
||||||
|
if build['version'] != asset_json['version']
|
||||||
|
]
|
||||||
|
# Add the new build
|
||||||
|
list_data['builds'].append(asset_json)
|
||||||
|
|
||||||
|
# Update releases
|
||||||
|
version = asset_json['version']
|
||||||
|
list_data['releases'][version] = f"{asset_json['name']}+{asset_json['longVersion']}"
|
||||||
|
|
||||||
|
# Update latest release
|
||||||
|
list_data['latestRelease'] = version
|
||||||
|
|
||||||
|
with open(list_file_path, 'w') as f:
|
||||||
|
json.dump(list_data, f, indent=4)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Validate arguments
|
||||||
|
if len(sys.argv) != 3:
|
||||||
|
print("Usage: python script.py <repo> <tag>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
repo, tag = sys.argv[1], sys.argv[2]
|
||||||
|
|
||||||
|
# Validate GitHub token
|
||||||
|
validate_github_token()
|
||||||
|
|
||||||
|
# Fetch release data
|
||||||
|
release_data = fetch_release_data(repo, tag)
|
||||||
|
|
||||||
|
# Fetch checksums
|
||||||
|
checksums = fetch_checksum_file(release_data)
|
||||||
|
|
||||||
|
# Mapping of asset names to platform folders
|
||||||
|
platform_mapping = {
|
||||||
|
'resolc-x86_64-unknown-linux-musl': 'linux',
|
||||||
|
'resolc-universal-apple-darwin': 'macos',
|
||||||
|
'resolc-x86_64-pc-windows-msvc.exe': 'windows',
|
||||||
|
'resolc_web.js': 'wasm'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Process each asset
|
||||||
|
for asset in release_data['assets']:
|
||||||
|
platform_name = platform_mapping.get(asset['name'])
|
||||||
|
if platform_name:
|
||||||
|
platform_folder = os.path.join(platform_name)
|
||||||
|
asset_json = generate_asset_json(release_data, asset, checksums)
|
||||||
|
save_platform_json(platform_folder, asset_json, tag)
|
||||||
|
print(f"Processed {asset['name']} for {platform_name}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
This script generates JSON files for different platforms based on GitHub data.
|
||||||
|
Requires the GITHUB_SHA, FIRST_SOLC_VERSION, LAST_SOLC_VERSION, TAG and FILEPATH environment variables to be set.
|
||||||
|
Usage:
|
||||||
|
python json_generator_nightly.py
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
def validate_env_variables():
|
||||||
|
"""Validate that environment variables are set."""
|
||||||
|
if "GITHUB_SHA" not in os.environ:
|
||||||
|
print("Error: GITHUB_SHA environment variable is not set.")
|
||||||
|
sys.exit(1)
|
||||||
|
if "FIRST_SOLC_VERSION" not in os.environ:
|
||||||
|
print("Error: FIRST_SOLC_VERSION environment variable is not set.")
|
||||||
|
sys.exit(1)
|
||||||
|
if "LAST_SOLC_VERSION" not in os.environ:
|
||||||
|
print("Error: LAST_SOLC_VERSION environment variable is not set.")
|
||||||
|
sys.exit(1)
|
||||||
|
if "TAG" not in os.environ:
|
||||||
|
print("Error: TAG environment variable is not set.")
|
||||||
|
sys.exit(1)
|
||||||
|
if "FILEPATH" not in os.environ:
|
||||||
|
print("Error: FILEPATH environment variable is not set.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_data_file():
|
||||||
|
"""
|
||||||
|
Fetch the data.json file with artifacts urls and sha256 checksums
|
||||||
|
and parse it into a single dictionary mapping artifact names to their URLs and SHAs.
|
||||||
|
"""
|
||||||
|
# read data.json file
|
||||||
|
artifacts_data = {}
|
||||||
|
data_file_path = os.environ["FILEPATH"]
|
||||||
|
if not os.path.exists(data_file_path):
|
||||||
|
print("Error: data.json file not found.")
|
||||||
|
sys.exit(1)
|
||||||
|
with open(data_file_path, 'r') as f:
|
||||||
|
try:
|
||||||
|
artifacts_data = json.load(f)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
print("Error: data.json file is not a valid JSON.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
for item in artifacts_data:
|
||||||
|
for key, value in item.items():
|
||||||
|
if key.endswith('_url'):
|
||||||
|
base_key = key.rsplit('_url', 1)[0]
|
||||||
|
if base_key not in result:
|
||||||
|
result[base_key] = {}
|
||||||
|
result[base_key]['url'] = value
|
||||||
|
elif key.endswith('_sha'):
|
||||||
|
base_key = key.rsplit('_sha', 1)[0]
|
||||||
|
if base_key not in result:
|
||||||
|
result[base_key] = {}
|
||||||
|
result[base_key]['sha'] = value
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def extract_build_hash():
|
||||||
|
"""Extract the first 8 characters of the commit hash."""
|
||||||
|
sha = os.environ.get("GITHUB_SHA")
|
||||||
|
return f"commit.{sha[:8]}"
|
||||||
|
|
||||||
|
def generate_asset_json_nightly(name, url, checksum):
|
||||||
|
"""Generate JSON for a specific asset."""
|
||||||
|
# Date in format YYYY-MM-DD
|
||||||
|
date = datetime.now().strftime("%Y.%-m.%-d")
|
||||||
|
last_version = os.environ.get("TAG").replace('v','')
|
||||||
|
version = f"{last_version}-nightly.{date}"
|
||||||
|
SHA = os.environ.get("GITHUB_SHA", "")[:8]
|
||||||
|
build = f"commit.{SHA}"
|
||||||
|
long_version = f"{version}+{build}"
|
||||||
|
|
||||||
|
return {
|
||||||
|
"name": name,
|
||||||
|
"version": version,
|
||||||
|
"build": build,
|
||||||
|
"longVersion": long_version,
|
||||||
|
"url": url,
|
||||||
|
"sha256": checksum,
|
||||||
|
"firstSolcVersion": os.environ.get("FIRST_SOLC_VERSION"),
|
||||||
|
"lastSolcVersion": os.environ.get("LAST_SOLC_VERSION")
|
||||||
|
}
|
||||||
|
|
||||||
|
def save_platform_json(platform_folder, asset_json):
|
||||||
|
"""Save asset JSON and update list.json for a specific platform."""
|
||||||
|
# Create platform folder if it doesn't exist
|
||||||
|
os.makedirs(platform_folder, exist_ok=True)
|
||||||
|
|
||||||
|
# Update or create list.json
|
||||||
|
list_file_path = os.path.join(platform_folder, "list.json")
|
||||||
|
|
||||||
|
if os.path.exists(list_file_path):
|
||||||
|
with open(list_file_path, 'r') as f:
|
||||||
|
try:
|
||||||
|
list_data = json.load(f)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
list_data = {"builds": [], "releases": {}, "latestRelease": ""}
|
||||||
|
else:
|
||||||
|
list_data = {"builds": [], "releases": {}, "latestRelease": ""}
|
||||||
|
|
||||||
|
# Remove any existing entry with the same path
|
||||||
|
list_data['builds'] = [
|
||||||
|
build for build in list_data['builds']
|
||||||
|
if build['version'] != asset_json['version']
|
||||||
|
]
|
||||||
|
# Add the new build
|
||||||
|
list_data['builds'].append(asset_json)
|
||||||
|
|
||||||
|
# Update releases
|
||||||
|
version = asset_json['version']
|
||||||
|
list_data['releases'][version] = f"{asset_json['name']}+{asset_json['longVersion']}"
|
||||||
|
|
||||||
|
# Update latest release
|
||||||
|
list_data['latestRelease'] = version
|
||||||
|
|
||||||
|
with open(list_file_path, 'w') as f:
|
||||||
|
json.dump(list_data, f, indent=4)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
validate_env_variables()
|
||||||
|
data = fetch_data_file()
|
||||||
|
|
||||||
|
# Mapping of asset names to platform folders
|
||||||
|
platform_mapping = {
|
||||||
|
'resolc-x86_64-unknown-linux-musl': 'linux',
|
||||||
|
'resolc-universal-apple-darwin': 'macos',
|
||||||
|
'resolc-x86_64-pc-windows-msvc': 'windows',
|
||||||
|
'resolc-web.js': 'wasm'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Process each asset
|
||||||
|
for asset in data.keys():
|
||||||
|
platform_name = platform_mapping.get(asset)
|
||||||
|
if platform_name:
|
||||||
|
platform_folder = os.path.join(platform_name)
|
||||||
|
asset_json = generate_asset_json_nightly(asset, data[asset]['url'], data[asset]['sha'])
|
||||||
|
save_platform_json(platform_folder, asset_json)
|
||||||
|
print(f"Processed {asset} for {platform_name}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
name: Check docs up-to-date
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
paths:
|
||||||
|
- 'book/**'
|
||||||
|
- 'docs/**'
|
||||||
|
- '.gitignore'
|
||||||
|
- 'Makefile'
|
||||||
|
- '.github/workflows/book.yml'
|
||||||
|
pull_request:
|
||||||
|
branches: ["main"]
|
||||||
|
types: [opened, synchronize]
|
||||||
|
paths:
|
||||||
|
- 'book/**'
|
||||||
|
- 'docs/**'
|
||||||
|
- '.gitignore'
|
||||||
|
- 'Makefile'
|
||||||
|
- '.github/workflows/book.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-docs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with: { fetch-depth: 0 }
|
||||||
|
|
||||||
|
- name: Install and test the mdBook
|
||||||
|
run: make test-book
|
||||||
|
|
||||||
|
- name: Build book
|
||||||
|
run: mdbook build book
|
||||||
|
|
||||||
|
- name: Build book to tmp
|
||||||
|
run: mdbook build book -d docs-tmp
|
||||||
|
|
||||||
|
- name: Compare with committed docs
|
||||||
|
run: |
|
||||||
|
if ! diff -r docs-tmp docs 2>/dev/null; then
|
||||||
|
echo "docs/ is not up-to-date. Run make test-book and commit the docs/ directory"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
name: Generate JSON for resolc-bin
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
generateJson:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: contains(github.event.release.tag_name, 'llvm') == false
|
||||||
|
environment: tags
|
||||||
|
env:
|
||||||
|
# the token is needed for json_generator.py
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: tmp
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: paritytech/resolc-bin
|
||||||
|
path: resolc-bin
|
||||||
|
|
||||||
|
- uses: actions/create-github-app-token@v1
|
||||||
|
id: app-token
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.REVIVE_JSON_APP_ID }}
|
||||||
|
private-key: ${{ secrets.REVIVE_JSON_APP_KEY }}
|
||||||
|
owner: paritytech
|
||||||
|
repositories: resolc-bin
|
||||||
|
|
||||||
|
- name: Generate json and push
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ steps.app-token.outputs.token }}
|
||||||
|
APP_NAME: "paritytech-revive-json"
|
||||||
|
Green: "\e[32m"
|
||||||
|
NC: "\e[0m"
|
||||||
|
run: |
|
||||||
|
sudo apt-get update && sudo apt-get install -y wget
|
||||||
|
wget https://github.com/${GITHUB_REPOSITORY}/releases/download/${GITHUB_REF_NAME}/resolc-x86_64-unknown-linux-musl
|
||||||
|
chmod +x resolc-x86_64-unknown-linux-musl
|
||||||
|
export FIRST_SOLC_VERSION=$(./resolc-x86_64-unknown-linux-musl --supported-solc-versions | cut -f 1 -d "," | tr -d ">=")
|
||||||
|
export LAST_SOLC_VERSION=$(./resolc-x86_64-unknown-linux-musl --supported-solc-versions | cut -f 2 -d "," | tr -d "<=")
|
||||||
|
|
||||||
|
cd resolc-bin
|
||||||
|
python ../tmp/.github/scripts/json_generator.py ${GITHUB_REPOSITORY} ${{ github.event.release.tag_name }}
|
||||||
|
|
||||||
|
echo "${Green}Add new remote with gh app token${NC}"
|
||||||
|
git remote set-url origin $(git config remote.origin.url | sed "s/github.com/${APP_NAME}:${TOKEN}@github.com/g")
|
||||||
|
|
||||||
|
echo "${Green}Remove http section that causes issues with gh app auth token${NC}"
|
||||||
|
sed -i.bak '/\[http/d' ./.git/config
|
||||||
|
sed -i.bak '/extraheader/d' ./.git/config
|
||||||
|
|
||||||
|
git config user.email "ci@parity.io"
|
||||||
|
git config user.name "${APP_NAME}"
|
||||||
|
|
||||||
|
git add .
|
||||||
|
git commit -m "Update json"
|
||||||
|
git push origin main
|
||||||
|
|
||||||
|
echo "::notice::info.list files were successfully published to https://github.com/paritytech/resolc-bin"
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
name: Release LLVM
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
llvm_version:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: llvm version in "x.x.x" format, e.g. "18.1.8"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
create-release-draft:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.resolve-version.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- id: resolve-version
|
||||||
|
run: |
|
||||||
|
echo "version=llvm-${{ inputs.llvm_version }}-revive.${GITHUB_SHA:0:7}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
name: ${{ steps.resolve-version.outputs.version }}
|
||||||
|
body: "LLVM is a dependency of revive. The LLVM releases are used by our CI to build revive."
|
||||||
|
draft: true
|
||||||
|
tag_name: ${{ steps.resolve-version.outputs.version }}
|
||||||
|
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
target: [x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl, wasm32-unknown-emscripten, aarch64-apple-darwin, x86_64-apple-darwin, x86_64-pc-windows-msvc]
|
||||||
|
include:
|
||||||
|
- target: x86_64-unknown-linux-gnu
|
||||||
|
builder-arg: gnu
|
||||||
|
host: linux
|
||||||
|
runner: parity-large
|
||||||
|
- target: x86_64-unknown-linux-musl
|
||||||
|
builder-arg: musl
|
||||||
|
host: linux
|
||||||
|
runner: parity-large
|
||||||
|
- target: wasm32-unknown-emscripten
|
||||||
|
builder-arg: emscripten
|
||||||
|
host: linux
|
||||||
|
runner: parity-large
|
||||||
|
- target: aarch64-apple-darwin
|
||||||
|
builder-arg: gnu
|
||||||
|
host: macos
|
||||||
|
runner: macos-15
|
||||||
|
- target: x86_64-apple-darwin
|
||||||
|
builder-arg: gnu
|
||||||
|
host: macos
|
||||||
|
# `macos-15-intel` will be the last x86_64 `macos` image supported by GHA.
|
||||||
|
# It will be available until Aug. 2027 (see https://github.com/actions/runner-images/issues/13045).
|
||||||
|
runner: macos-15-intel
|
||||||
|
- target: x86_64-pc-windows-msvc
|
||||||
|
builder-arg: gnu
|
||||||
|
host: windows
|
||||||
|
runner: windows-2022
|
||||||
|
needs: create-release-draft
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
env:
|
||||||
|
RUST_LOG: trace
|
||||||
|
permissions:
|
||||||
|
contents: write # for uploading assets to release
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
|
with:
|
||||||
|
# without this it will override our rust flags
|
||||||
|
rustflags: ""
|
||||||
|
cache-key: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
if: ${{ matrix.host == 'linux' }}
|
||||||
|
run: |
|
||||||
|
cat /etc/apt/sources.list
|
||||||
|
sudo sed -i 's/jammy/noble/g' /etc/apt/sources.list
|
||||||
|
cat /etc/apt/sources.list
|
||||||
|
sudo apt-get update && sudo apt-get install -y cmake ninja-build curl git libssl-dev pkg-config clang lld musl xz-utils libc6-dev gcc-multilib
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
if: ${{ matrix.host == 'macos' }}
|
||||||
|
run: |
|
||||||
|
brew install ninja
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
if: ${{ matrix.host == 'windows' }}
|
||||||
|
run: |
|
||||||
|
choco install ninja
|
||||||
|
|
||||||
|
- name: Install LLVM Builder
|
||||||
|
run: |
|
||||||
|
cargo install --locked --force --path crates/llvm-builder
|
||||||
|
|
||||||
|
- name: Build LLVM
|
||||||
|
if: ${{ matrix.target != 'wasm32-unknown-emscripten' }}
|
||||||
|
run: |
|
||||||
|
revive-llvm --target-env ${{ matrix.builder-arg }} build --llvm-projects lld --llvm-projects clang
|
||||||
|
|
||||||
|
- name: Build LLVM
|
||||||
|
if: ${{ matrix.target == 'wasm32-unknown-emscripten' }}
|
||||||
|
run: |
|
||||||
|
revive-llvm emsdk
|
||||||
|
source emsdk/emsdk_env.sh
|
||||||
|
revive-llvm --target-env ${{ matrix.builder-arg }} build --llvm-projects lld
|
||||||
|
|
||||||
|
- name: Remove Unnecessary Binaries
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd target-llvm/${{ matrix.builder-arg }}/target-final/bin/
|
||||||
|
rm -f diagtool* llvm-libtool-darwin* llvm-lipo* llvm-pdbutil* llvm-dwarfdump* llvm-nm* llvm-readobj* llvm-cfi-verify* \
|
||||||
|
sancov* llvm-debuginfo-analyzer* llvm-objdump* llvm-profgen* llvm-extract* llvm-jitlink* llvm-c-test* llvm-gsymutil* llvm-dwp* \
|
||||||
|
dsymutil* llvm-dwarfutil* llvm-exegesis* lli clang-rename* bugpoint* clang-extdef-mapping* clang-refactor* c-index-test* \
|
||||||
|
llvm-reduce* llvm-lto* clang-linker-wrapper* llc* llvm-lto2* llvm-otool* llvm-readelf* \
|
||||||
|
clang-repl* clang-check* clang-scan-deps*
|
||||||
|
cd -
|
||||||
|
|
||||||
|
- name: Package Artifact
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mv target-llvm/${{ matrix.builder-arg }}/target-final/ llvm-${{ matrix.target }}
|
||||||
|
tar -czf "${{ needs.create-release-draft.outputs.version }}-${{ matrix.target }}.tar.gz" llvm-${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Add Artifact to Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: ${{ needs.create-release-draft.outputs.version }}
|
||||||
|
draft: true
|
||||||
|
files: |
|
||||||
|
${{ needs.create-release-draft.outputs.version }}-${{ matrix.target }}.tar.gz
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
name: Nightly Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Run every day at 01:00 UTC
|
||||||
|
- cron: "0 1 * * *"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check_commits:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
has_commits: ${{ steps.check_commits.outputs.has_commits }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: "main"
|
||||||
|
|
||||||
|
- name: Check for commits from yesterday
|
||||||
|
id: check_commits
|
||||||
|
run: |
|
||||||
|
# Get yesterday's date in YYYY-MM-DD format
|
||||||
|
YESTERDAY=$(date -d "yesterday" +%Y-%m-%d)
|
||||||
|
echo "Checking for commits from: $YESTERDAY"
|
||||||
|
|
||||||
|
# Check if there were any commits yesterday
|
||||||
|
COMMIT_COUNT=$(git log --oneline --since="$YESTERDAY 00:00:00" --until="$YESTERDAY 23:59:59" | wc -l)
|
||||||
|
|
||||||
|
echo "Found $COMMIT_COUNT commits from yesterday"
|
||||||
|
|
||||||
|
if [ $COMMIT_COUNT -gt 0 ]; then
|
||||||
|
echo "has_commits=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "✅ Found $COMMIT_COUNT commits from yesterday - continuing workflow"
|
||||||
|
else
|
||||||
|
echo "has_commits=false" >> $GITHUB_OUTPUT
|
||||||
|
echo "❌ No commits found from yesterday - skipping remaining steps"
|
||||||
|
echo "::notice::❌ No commits found from yesterday - skipping remaining steps"
|
||||||
|
fi
|
||||||
|
|
||||||
|
build:
|
||||||
|
if: ${{ needs.check_commits.outputs.has_commits == 'true' }}
|
||||||
|
needs: [check_commits]
|
||||||
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
|
with:
|
||||||
|
is_release: false
|
||||||
|
retention_days: 40
|
||||||
|
|
||||||
|
create-macos-fat-binary:
|
||||||
|
if: ${{ needs.check_commits.outputs.has_commits == 'true' }}
|
||||||
|
needs: [build]
|
||||||
|
outputs:
|
||||||
|
resolc-universal-apple-darwin_url: ${{ steps.set-output.outputs.resolc-universal-apple-darwin_url }}
|
||||||
|
resolc-universal-apple-darwin_sha: ${{ steps.set-output.outputs.resolc-universal-apple-darwin_sha }}
|
||||||
|
runs-on: macos-15
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Create macOS Fat Binary
|
||||||
|
run: |
|
||||||
|
lipo resolc-aarch64-apple-darwin resolc-x86_64-apple-darwin -create -output resolc-universal-apple-darwin
|
||||||
|
|
||||||
|
- name: Make Executable
|
||||||
|
run: |
|
||||||
|
chmod +x resolc-universal-apple-darwin
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
id: artifact-upload-step
|
||||||
|
with:
|
||||||
|
name: resolc-universal-apple-darwin
|
||||||
|
path: resolc-universal-apple-darwin
|
||||||
|
retention-days: 40
|
||||||
|
|
||||||
|
- name: Set output variables
|
||||||
|
id: set-output
|
||||||
|
env:
|
||||||
|
TARGET: resolc-universal-apple-darwin
|
||||||
|
run: |
|
||||||
|
echo "${TARGET}_url=${{ steps.artifact-upload-step.outputs.artifact-url }}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "${TARGET}_sha=${{ steps.artifact-upload-step.outputs.artifact-digest }}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
generate-nightly-json:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
if: ${{ needs.check_commits.outputs.has_commits == 'true' }}
|
||||||
|
environment: tags
|
||||||
|
needs: [build, create-macos-fat-binary, check_commits]
|
||||||
|
steps:
|
||||||
|
- name: Checkout revive
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: revive
|
||||||
|
|
||||||
|
- name: Checkout resolc-bin
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: paritytech/resolc-bin
|
||||||
|
path: resolc-bin
|
||||||
|
|
||||||
|
- name: Download Artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
merge-multiple: true
|
||||||
|
path: bins
|
||||||
|
|
||||||
|
- uses: actions/create-github-app-token@v1
|
||||||
|
id: app-token
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.REVIVE_JSON_APP_ID }}
|
||||||
|
private-key: ${{ secrets.REVIVE_JSON_APP_KEY }}
|
||||||
|
owner: paritytech
|
||||||
|
repositories: resolc-bin
|
||||||
|
|
||||||
|
- name: Generate JSON
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
TOKEN: ${{ steps.app-token.outputs.token }}
|
||||||
|
APP_NAME: "paritytech-revive-json"
|
||||||
|
Green: "\e[32m"
|
||||||
|
NC: "\e[0m"
|
||||||
|
run: |
|
||||||
|
echo '[' > data.json
|
||||||
|
echo '${{ toJSON(needs.build.outputs) }}' >> data.json
|
||||||
|
echo ',' >> data.json
|
||||||
|
echo '${{ toJSON(needs.create-macos-fat-binary.outputs) }}' >> data.json
|
||||||
|
echo ']' >> data.json
|
||||||
|
chmod +x bins/resolc-x86_64-unknown-linux-musl
|
||||||
|
export FIRST_SOLC_VERSION=$(./bins/resolc-x86_64-unknown-linux-musl --supported-solc-versions | cut -f 1 -d "," | tr -d ">=")
|
||||||
|
export LAST_SOLC_VERSION=$(./bins/resolc-x86_64-unknown-linux-musl --supported-solc-versions | cut -f 2 -d "," | tr -d "<=")
|
||||||
|
export FILEPATH=$(readlink -f data.json)
|
||||||
|
export TAG=$(cd revive;gh release list --json name,isLatest --jq '.[] | select(.isLatest)|.name')
|
||||||
|
cd resolc-bin
|
||||||
|
mkdir -p nightly
|
||||||
|
cd nightly
|
||||||
|
python3 ../../revive/.github/scripts/json_generator_nightly.py
|
||||||
|
cd ..
|
||||||
|
git status
|
||||||
|
|
||||||
|
echo "${Green}Add new remote with gh app token${NC}"
|
||||||
|
git remote set-url origin $(git config remote.origin.url | sed "s/github.com/${APP_NAME}:${TOKEN}@github.com/g")
|
||||||
|
|
||||||
|
echo "${Green}Remove http section that causes issues with gh app auth token${NC}"
|
||||||
|
sed -i.bak '/\[http/d' ./.git/config
|
||||||
|
sed -i.bak '/extraheader/d' ./.git/config
|
||||||
|
|
||||||
|
git config user.email "ci@parity.io"
|
||||||
|
git config user.name "${APP_NAME}"
|
||||||
|
|
||||||
|
git add nightly/
|
||||||
|
git commit -m "Update nightly json"
|
||||||
|
git push origin main
|
||||||
|
|
||||||
|
echo "::notice::nightly info.list files were successfully published to https://github.com/paritytech/resolc-bin"
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
name: Build & Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
pull_request:
|
||||||
|
branches: ["main"]
|
||||||
|
types: [opened, synchronize, labeled, unlabeled]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-version-changed:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
env:
|
||||||
|
CURRENT_TAG: ${{ github.ref_name }}
|
||||||
|
outputs:
|
||||||
|
RELEASE_NOTES: ${{ steps.versions.outputs.RELEASE_NOTES }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Check versions
|
||||||
|
id: versions
|
||||||
|
run: |
|
||||||
|
if [[ $CURRENT_TAG == 'main' ]]; then
|
||||||
|
echo "::notice::Tag $CURRENT_TAG is not a release tag, skipping the check in the main branch"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $CURRENT_TAG != "v"* ]]; then
|
||||||
|
echo "::notice::Tag $CURRENT_TAG is not a release tag, skipping the check in a PR"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
export PKG_VER=v$(cat crates/resolc/Cargo.toml | grep -A 5 package] | grep version | cut -d '=' -f 2 | tr -d '"' | tr -d " ")
|
||||||
|
echo "Current tag $CURRENT_TAG"
|
||||||
|
echo "Package version $PKG_VER"
|
||||||
|
|
||||||
|
if [[ $CURRENT_TAG != $PKG_VER ]]; then
|
||||||
|
echo "::error::Tag $CURRENT_TAG doesn't match package version $PKG_VER in Cargo.toml, please fix"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export RELEASE_NOTES="$(sed '/^## '${CURRENT_TAG}'/,/^## v/!d' CHANGELOG.md | sed -e '1d' -e '$d')"
|
||||||
|
|
||||||
|
echo "Release notes:"
|
||||||
|
echo "$RELEASE_NOTES"
|
||||||
|
|
||||||
|
echo 'RELEASE_NOTES<<EOF' >> $GITHUB_OUTPUT
|
||||||
|
echo "$RELEASE_NOTES" >> $GITHUB_OUTPUT
|
||||||
|
echo 'EOF' >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: [check-version-changed]
|
||||||
|
uses: ./.github/workflows/reusable-build.yml
|
||||||
|
with:
|
||||||
|
is_release: true
|
||||||
|
retention_days: 1
|
||||||
|
|
||||||
|
create-release:
|
||||||
|
if: startsWith(github.ref_name, 'v')
|
||||||
|
needs: [check-version-changed, build]
|
||||||
|
runs-on: macos-15
|
||||||
|
environment: tags
|
||||||
|
steps:
|
||||||
|
- name: Download Artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Create macOS Fat Binary
|
||||||
|
run: |
|
||||||
|
lipo resolc-aarch64-apple-darwin resolc-x86_64-apple-darwin -create -output resolc-universal-apple-darwin
|
||||||
|
|
||||||
|
- name: Make Executable
|
||||||
|
run: |
|
||||||
|
chmod +x resolc-x86_64-unknown-linux-musl
|
||||||
|
chmod +x resolc-universal-apple-darwin
|
||||||
|
|
||||||
|
- name: Create sha-256 checksum
|
||||||
|
run: |
|
||||||
|
shasum -a 256 resolc-x86_64-unknown-linux-musl > checksums.txt
|
||||||
|
shasum -a 256 resolc-universal-apple-darwin >> checksums.txt
|
||||||
|
shasum -a 256 resolc-x86_64-pc-windows-msvc.exe >> checksums.txt
|
||||||
|
shasum -a 256 resolc.js >> checksums.txt
|
||||||
|
shasum -a 256 resolc.wasm >> checksums.txt
|
||||||
|
shasum -a 256 resolc_web.js >> checksums.txt
|
||||||
|
|
||||||
|
- uses: actions/create-github-app-token@v1
|
||||||
|
id: app-token
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.REVIVE_RELEASE_APP_ID }}
|
||||||
|
private-key: ${{ secrets.REVIVE_RELEASE_APP_KEY }}
|
||||||
|
|
||||||
|
- name: create-release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
body: |
|
||||||
|
## Changelog
|
||||||
|
${{ needs.check-version-changed.outputs.RELEASE_NOTES }}
|
||||||
|
|
||||||
|
## Note for macOS Users
|
||||||
|
The macOS binary is unsigned and it needs to be made runnable using `xattr -c resolc-universal-apple-darwin`.
|
||||||
|
tag_name: ${{ github.ref_name }}
|
||||||
|
name: ${{ github.ref_name }}
|
||||||
|
prerelease: true
|
||||||
|
token: ${{ steps.app-token.outputs.token }}
|
||||||
|
target_commitish: ${{ github.sha }}
|
||||||
|
files: |
|
||||||
|
resolc-x86_64-unknown-linux-musl
|
||||||
|
resolc-universal-apple-darwin
|
||||||
|
resolc-x86_64-pc-windows-msvc.exe
|
||||||
|
resolc.js
|
||||||
|
resolc.wasm
|
||||||
|
resolc_web.js
|
||||||
|
checksums.txt
|
||||||
|
|
||||||
|
npm-release:
|
||||||
|
needs: [create-release]
|
||||||
|
runs-on: macos-15
|
||||||
|
environment: tags
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download Artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Set Up Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: "20"
|
||||||
|
|
||||||
|
- run: npm ci -w js/resolc
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
cp -f resolc.{wasm,js} js/resolc/src/resolc
|
||||||
|
npm -w js/resolc run build
|
||||||
|
|
||||||
|
- name: npm pack
|
||||||
|
run: npm -w js/resolc pack
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: npm_package
|
||||||
|
path: "parity-resolc-*.tgz"
|
||||||
|
|
||||||
|
- uses: octokit/request-action@bbedc70b1981e610d89f1f8de88311a1fc02fb83
|
||||||
|
with:
|
||||||
|
route: POST /repos/paritytech/npm_publish_automation/actions/workflows/publish.yml/dispatches
|
||||||
|
ref: main
|
||||||
|
inputs: '${{ format(''{{ "artifact_name": "npm_package", "repo": "{0}", "run_id": "{1}" }}'', github.repository, github.run_id) }}'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.NPM_PUBLISH_AUTOMATION_TOKEN }}
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
name: Reusable Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
is_release:
|
||||||
|
description: "Whether this is a release build"
|
||||||
|
required: true
|
||||||
|
type: boolean
|
||||||
|
retention_days:
|
||||||
|
description: "Artifact retention days"
|
||||||
|
required: false
|
||||||
|
type: number
|
||||||
|
default: 1
|
||||||
|
outputs:
|
||||||
|
resolc-x86_64-unknown-linux-musl_url:
|
||||||
|
value: ${{ jobs.build.outputs.resolc-x86_64-unknown-linux-musl_url }}
|
||||||
|
resolc-x86_64-unknown-linux-musl_sha:
|
||||||
|
value: ${{ jobs.build.outputs.resolc-x86_64-unknown-linux-musl_sha }}
|
||||||
|
resolc-aarch64-apple-darwin_url:
|
||||||
|
value: ${{ jobs.build.outputs.resolc-aarch64-apple-darwin_url }}
|
||||||
|
resolc-aarch64-apple-darwin_sha:
|
||||||
|
value: ${{ jobs.build.outputs.resolc-aarch64-apple-darwin_sha }}
|
||||||
|
resolc-x86_64-apple-darwin_url:
|
||||||
|
value: ${{ jobs.build.outputs.resolc-x86_64-apple-darwin_url }}
|
||||||
|
resolc-x86_64-apple-darwin_sha:
|
||||||
|
value: ${{ jobs.build.outputs.resolc-x86_64-apple-darwin_sha }}
|
||||||
|
resolc-x86_64-pc-windows-msvc_url:
|
||||||
|
value: ${{ jobs.build.outputs.resolc-x86_64-pc-windows-msvc_url }}
|
||||||
|
resolc-x86_64-pc-windows-msvc_sha:
|
||||||
|
value: ${{ jobs.build.outputs.resolc-x86_64-pc-windows-msvc_sha }}
|
||||||
|
resolc-web_js_url:
|
||||||
|
value: ${{ jobs.build-wasm.outputs.resolc_web_js_url }}
|
||||||
|
resolc-web_js_sha:
|
||||||
|
value: ${{ jobs.build-wasm.outputs.resolc_web_js_sha }}
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
RUST_MUSL_CROSS_IMAGE: messense/rust-musl-cross@sha256:c0154e992adb791c3b848dd008939d19862549204f8cb26f5ca7a00f629e6067
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
# github actions matrix jobs don't support multiple outputs
|
||||||
|
# ugly workaround from https://github.com/orgs/community/discussions/17245#discussioncomment-11222880
|
||||||
|
outputs:
|
||||||
|
resolc-x86_64-unknown-linux-musl_url: ${{ steps.set-output.outputs.resolc-x86_64-unknown-linux-musl_url }}
|
||||||
|
resolc-x86_64-unknown-linux-musl_sha: ${{ steps.set-output.outputs.resolc-x86_64-unknown-linux-musl_sha }}
|
||||||
|
resolc-aarch64-apple-darwin_url: ${{ steps.set-output.outputs.resolc-aarch64-apple-darwin_url }}
|
||||||
|
resolc-aarch64-apple-darwin_sha: ${{ steps.set-output.outputs.resolc-aarch64-apple-darwin_sha }}
|
||||||
|
resolc-x86_64-apple-darwin_url: ${{ steps.set-output.outputs.resolc-x86_64-apple-darwin_url }}
|
||||||
|
resolc-x86_64-apple-darwin_sha: ${{ steps.set-output.outputs.resolc-x86_64-apple-darwin_sha }}
|
||||||
|
resolc-x86_64-pc-windows-msvc_url: ${{ steps.set-output.outputs.resolc-x86_64-pc-windows-msvc_url }}
|
||||||
|
resolc-x86_64-pc-windows-msvc_sha: ${{ steps.set-output.outputs.resolc-x86_64-pc-windows-msvc_sha }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
target:
|
||||||
|
[
|
||||||
|
x86_64-unknown-linux-musl,
|
||||||
|
aarch64-apple-darwin,
|
||||||
|
x86_64-apple-darwin,
|
||||||
|
x86_64-pc-windows-msvc,
|
||||||
|
]
|
||||||
|
include:
|
||||||
|
- target: x86_64-unknown-linux-musl
|
||||||
|
type: musl
|
||||||
|
runner: ubuntu-24.04
|
||||||
|
- target: aarch64-apple-darwin
|
||||||
|
type: native
|
||||||
|
runner: macos-15
|
||||||
|
- target: x86_64-apple-darwin
|
||||||
|
type: native
|
||||||
|
# `macos-15-intel` will be the last x86_64 `macos` image supported by GHA.
|
||||||
|
# It will be available until Aug. 2027 (see https://github.com/actions/runner-images/issues/13045).
|
||||||
|
runner: macos-15-intel
|
||||||
|
- target: x86_64-pc-windows-msvc
|
||||||
|
type: native
|
||||||
|
runner: windows-2022
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
|
with:
|
||||||
|
rustflags: ""
|
||||||
|
cache-key: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Download LLVM
|
||||||
|
uses: ./.github/actions/get-llvm
|
||||||
|
with:
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Build (Native)
|
||||||
|
if: ${{ matrix.type == 'native' }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
export LLVM_SYS_181_PREFIX=$PWD/llvm-${{ matrix.target }}
|
||||||
|
make install-bin
|
||||||
|
mv target/release/resolc resolc-${{ matrix.target }} || mv target/release/resolc.exe resolc-${{ matrix.target }}.exe
|
||||||
|
|
||||||
|
- name: Build (MUSL)
|
||||||
|
if: ${{ matrix.type == 'musl' }}
|
||||||
|
run: |
|
||||||
|
docker run -v $PWD:/opt/revive $RUST_MUSL_CROSS_IMAGE /bin/bash -c "
|
||||||
|
cd /opt/revive
|
||||||
|
chown -R root:root .
|
||||||
|
apt update && apt upgrade -y && apt install -y pkg-config
|
||||||
|
export LLVM_SYS_181_PREFIX=/opt/revive/llvm-${{ matrix.target }}
|
||||||
|
make install-bin
|
||||||
|
mv target/${{ matrix.target }}/release/resolc resolc-${{ matrix.target }}
|
||||||
|
"
|
||||||
|
sudo chown -R $(id -u):$(id -g) .
|
||||||
|
|
||||||
|
- name: Install Solc
|
||||||
|
uses: ./.github/actions/get-solc
|
||||||
|
|
||||||
|
- name: Basic Sanity Check
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
result=$(./resolc-${{ matrix.target }} --bin crates/integration/contracts/flipper.sol)
|
||||||
|
echo $result
|
||||||
|
if [[ $result == *'50564d'* ]]; then exit 0; else exit 1; fi
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
id: artifact-upload-step
|
||||||
|
with:
|
||||||
|
name: resolc-${{ matrix.target }}
|
||||||
|
path: resolc-${{ matrix.target }}*
|
||||||
|
retention-days: ${{ inputs.retention_days }}
|
||||||
|
|
||||||
|
- name: Set output variables
|
||||||
|
if: ${{ !inputs.is_release }}
|
||||||
|
id: set-output
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "resolc-${{ matrix.target }}_url=${{ steps.artifact-upload-step.outputs.artifact-url }}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "resolc-${{ matrix.target }}_sha=${{ steps.artifact-upload-step.outputs.artifact-digest }}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
build-wasm:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
outputs:
|
||||||
|
resolc_web_js_url: ${{ steps.set-output.outputs.resolc_web_js_url }}
|
||||||
|
resolc_web_js_sha: ${{ steps.set-output.outputs.resolc_web_js_sha }}
|
||||||
|
env:
|
||||||
|
RELEASE_RESOLC_WASM_URI: https://github.com/paritytech/revive/releases/download/${{ github.ref_name }}/resolc.wasm
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
|
with:
|
||||||
|
target: wasm32-unknown-emscripten
|
||||||
|
rustflags: ""
|
||||||
|
|
||||||
|
- name: Download Host LLVM
|
||||||
|
uses: ./.github/actions/get-llvm
|
||||||
|
with:
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
|
||||||
|
- name: Download Wasm LLVM
|
||||||
|
uses: ./.github/actions/get-llvm
|
||||||
|
with:
|
||||||
|
target: wasm32-unknown-emscripten
|
||||||
|
|
||||||
|
- name: Download EMSDK
|
||||||
|
uses: ./.github/actions/get-emsdk
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
export LLVM_SYS_181_PREFIX=$PWD/llvm-x86_64-unknown-linux-gnu
|
||||||
|
export REVIVE_LLVM_TARGET_PREFIX=$PWD/llvm-wasm32-unknown-emscripten
|
||||||
|
source emsdk/emsdk_env.sh
|
||||||
|
make install-wasm
|
||||||
|
chmod -x ./target/wasm32-unknown-emscripten/release/resolc.wasm
|
||||||
|
|
||||||
|
- name: Set Up Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: "20"
|
||||||
|
|
||||||
|
- name: Basic Sanity Check
|
||||||
|
run: |
|
||||||
|
mkdir -p solc
|
||||||
|
curl -sSLo solc/soljson.js https://github.com/ethereum/solidity/releases/download/v0.8.31/soljson.js
|
||||||
|
node -e "
|
||||||
|
const soljson = require('solc/soljson');
|
||||||
|
const createRevive = require('./target/wasm32-unknown-emscripten/release/resolc.js');
|
||||||
|
|
||||||
|
const compiler = createRevive();
|
||||||
|
compiler.soljson = soljson;
|
||||||
|
|
||||||
|
const standardJsonInput =
|
||||||
|
{
|
||||||
|
language: 'Solidity',
|
||||||
|
sources: {
|
||||||
|
'MyContract.sol': {
|
||||||
|
content: 'pragma solidity ^0.8.0; contract MyContract { function greet() public pure returns (string memory) { return \'Hello\'; } }',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
settings: { optimizer: { enabled: false } }
|
||||||
|
};
|
||||||
|
|
||||||
|
compiler.writeToStdin(JSON.stringify(standardJsonInput));
|
||||||
|
compiler.callMain(['--standard-json']);
|
||||||
|
|
||||||
|
const stdout = compiler.readFromStdout();
|
||||||
|
const stderr = compiler.readFromStderr();
|
||||||
|
|
||||||
|
if (stderr) { console.error(stderr); process.exit(1); }
|
||||||
|
|
||||||
|
let out = JSON.parse(stdout);
|
||||||
|
let bytecode = out.contracts['MyContract.sol']['MyContract'].evm.bytecode.object
|
||||||
|
console.log(bytecode);
|
||||||
|
|
||||||
|
if(!bytecode.startsWith('50564d')) { process.exit(1); }
|
||||||
|
"
|
||||||
|
|
||||||
|
- name: Compress Artifact
|
||||||
|
run: |
|
||||||
|
mkdir -p resolc-wasm32-unknown-emscripten
|
||||||
|
mv ./target/wasm32-unknown-emscripten/release/resolc.js ./resolc-wasm32-unknown-emscripten/
|
||||||
|
mv ./target/wasm32-unknown-emscripten/release/resolc.wasm ./resolc-wasm32-unknown-emscripten/
|
||||||
|
mv ./target/wasm32-unknown-emscripten/release/resolc_web.js ./resolc-wasm32-unknown-emscripten/
|
||||||
|
|
||||||
|
- name: Upload artifacts (Release)
|
||||||
|
if: ${{ inputs.is_release }}
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: resolc-wasm32-unknown-emscripten
|
||||||
|
path: resolc-wasm32-unknown-emscripten/*
|
||||||
|
retention-days: ${{ inputs.retention_days }}
|
||||||
|
|
||||||
|
# There is no way to upload several files as several artifacts with a single upload-artifact step
|
||||||
|
# It's needed to have resolc_web.js separately for night builds for resolc-bin repo
|
||||||
|
# https://github.com/actions/upload-artifact/issues/331
|
||||||
|
- name: Upload artifact resolc.js (Nightly)
|
||||||
|
if: ${{ !inputs.is_release }}
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: resolc.js
|
||||||
|
path: resolc-wasm32-unknown-emscripten/resolc.js
|
||||||
|
retention-days: ${{ inputs.retention_days }}
|
||||||
|
|
||||||
|
- name: Upload artifacts resolc.wasm (Nightly)
|
||||||
|
if: ${{ !inputs.is_release }}
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: resolc.wasm
|
||||||
|
path: resolc-wasm32-unknown-emscripten/resolc.wasm
|
||||||
|
retention-days: ${{ inputs.retention_days }}
|
||||||
|
|
||||||
|
- name: Upload artifacts resolc_web.js (Nightly)
|
||||||
|
if: ${{ !inputs.is_release }}
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
id: artifact-upload-step
|
||||||
|
with:
|
||||||
|
name: resolc_web.js
|
||||||
|
path: resolc-wasm32-unknown-emscripten/resolc_web.js
|
||||||
|
retention-days: ${{ inputs.retention_days }}
|
||||||
|
|
||||||
|
- name: Set output variables
|
||||||
|
if: ${{ !inputs.is_release }}
|
||||||
|
id: set-output
|
||||||
|
env:
|
||||||
|
TARGET: resolc_web_js
|
||||||
|
run: |
|
||||||
|
echo "${TARGET}_url=${{ steps.artifact-upload-step.outputs.artifact-url }}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "${TARGET}_sha=${{ steps.artifact-upload-step.outputs.artifact-digest }}" >> "$GITHUB_OUTPUT"
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
name: Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: ["main"]
|
|
||||||
pull_request:
|
|
||||||
branches: ["main"]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-ubuntu-x86:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install solc
|
|
||||||
run: |
|
|
||||||
mkdir -p solc
|
|
||||||
curl -sSL --output solc/solc https://github.com/ethereum/solidity/releases/download/v0.8.26/solc-static-linux
|
|
||||||
chmod +x solc/solc
|
|
||||||
echo "$(pwd)/solc/" >> $GITHUB_PATH
|
|
||||||
|
|
||||||
- name: Install LLVM
|
|
||||||
run: |
|
|
||||||
curl -sSL --output llvm.tar.xz https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.4/clang+llvm-18.1.4-x86_64-linux-gnu-ubuntu-18.04.tar.xz
|
|
||||||
tar Jxf llvm.tar.xz
|
|
||||||
mv clang+llvm-18.1.4-x86_64-linux-gnu-ubuntu-18.04 llvm18/
|
|
||||||
echo "$(pwd)/llvm18/bin" >> $GITHUB_PATH
|
|
||||||
|
|
||||||
- name: Install apt dependencies
|
|
||||||
run: |
|
|
||||||
sudo add-apt-repository -y ppa:ethereum/ethereum
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install -y libtinfo5 ethereum
|
|
||||||
|
|
||||||
- name: Format
|
|
||||||
run: make format
|
|
||||||
|
|
||||||
- name: Clippy
|
|
||||||
run: make clippy
|
|
||||||
|
|
||||||
- name: Test cargo workspace
|
|
||||||
run: make test-workspace
|
|
||||||
|
|
||||||
- name: Test CLI
|
|
||||||
run: make test-cli
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: ${{ github.job }}-resolc
|
|
||||||
path: ./target/release/resolc
|
|
||||||
retention-days: 1
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
name: Test LLVM Builder
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: ["main"]
|
||||||
|
types: [opened, synchronize]
|
||||||
|
paths:
|
||||||
|
- 'crates/llvm-builder/**'
|
||||||
|
- '.github/workflows/test-llvm-builder.yml'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
runner: [parity-large, macos-15, windows-2022]
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
|
with:
|
||||||
|
# without this it will override our rust flags
|
||||||
|
rustflags: ""
|
||||||
|
cache-key: ${{ matrix.runner }}
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
if: matrix.runner == 'parity-large'
|
||||||
|
run: |
|
||||||
|
sudo apt update && sudo apt-get install -y cmake ninja-build curl git libssl-dev pkg-config clang lld musl
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
if: matrix.runner == 'macos-15'
|
||||||
|
run: |
|
||||||
|
brew install ninja
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: make test-llvm-builder
|
||||||
|
env:
|
||||||
|
RUST_LOG: trace
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
name: Test Wasm Version
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
pull_request:
|
||||||
|
branches: ["main"]
|
||||||
|
types: [opened, synchronize]
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
REVIVE_WASM_INSTALL_DIR: ${{ github.workspace }}/target/wasm32-unknown-emscripten/release
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
|
with:
|
||||||
|
target: wasm32-unknown-emscripten
|
||||||
|
# without this it will override our rust flags
|
||||||
|
rustflags: ""
|
||||||
|
|
||||||
|
- name: Download Host LLVM
|
||||||
|
uses: ./.github/actions/get-llvm
|
||||||
|
with:
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
|
||||||
|
- name: Download Wasm LLVM
|
||||||
|
uses: ./.github/actions/get-llvm
|
||||||
|
with:
|
||||||
|
target: wasm32-unknown-emscripten
|
||||||
|
|
||||||
|
- name: Install emsdk
|
||||||
|
uses: ./.github/actions/get-emsdk
|
||||||
|
|
||||||
|
- name: Set LLVM Environment Variables
|
||||||
|
run: |
|
||||||
|
echo "LLVM_SYS_181_PREFIX=$(pwd)/llvm-x86_64-unknown-linux-gnu" >> $GITHUB_ENV
|
||||||
|
echo "REVIVE_LLVM_TARGET_PREFIX=$(pwd)/llvm-wasm32-unknown-emscripten" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Build Revive
|
||||||
|
run: |
|
||||||
|
source emsdk/emsdk_env.sh
|
||||||
|
make install-wasm
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: revive-wasm
|
||||||
|
path: |
|
||||||
|
${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.js
|
||||||
|
${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.wasm
|
||||||
|
${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc_web.js
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
test:
|
||||||
|
needs: build
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: ["ubuntu-24.04", "macos-15", "windows-2022"]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Create Target Directory
|
||||||
|
run: mkdir -p ${{ env.REVIVE_WASM_INSTALL_DIR }}
|
||||||
|
|
||||||
|
- name: Download Artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: revive-wasm
|
||||||
|
path: ${{ env.REVIVE_WASM_INSTALL_DIR }}
|
||||||
|
|
||||||
|
- name: Set Up Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: "20"
|
||||||
|
|
||||||
|
- name: Install Node Packages
|
||||||
|
run: npm install
|
||||||
|
|
||||||
|
- name: Test emscripten
|
||||||
|
run: |
|
||||||
|
echo "Running tests for ${{ matrix.os }}"
|
||||||
|
npm run test:wasm
|
||||||
|
|
||||||
|
- name: Test @parity/resolc
|
||||||
|
run: |
|
||||||
|
echo "Running tests for ${{ matrix.os }}"
|
||||||
|
npm run -w js/resolc test
|
||||||
|
|
||||||
|
- name: Run Playwright tests
|
||||||
|
run: |
|
||||||
|
cd js/emscripten
|
||||||
|
npx playwright install --with-deps
|
||||||
|
npx playwright test
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
name: Test
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
pull_request:
|
||||||
|
branches: ["main"]
|
||||||
|
types: [opened, synchronize]
|
||||||
|
paths-ignore:
|
||||||
|
- "**.md"
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
|
with:
|
||||||
|
# without this it will override our rust flags
|
||||||
|
rustflags: ""
|
||||||
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
- name: Install Solc
|
||||||
|
uses: ./.github/actions/get-solc
|
||||||
|
|
||||||
|
- name: Download LLVM
|
||||||
|
uses: ./.github/actions/get-llvm
|
||||||
|
with:
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
|
||||||
|
- name: Set LLVM Environment Variables
|
||||||
|
run: |
|
||||||
|
echo "LLVM_SYS_181_PREFIX=$(pwd)/llvm-x86_64-unknown-linux-gnu" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Install Geth
|
||||||
|
run: |
|
||||||
|
sudo add-apt-repository -y ppa:ethereum/ethereum
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y ethereum
|
||||||
|
|
||||||
|
- name: Machete
|
||||||
|
uses: bnjbvr/cargo-machete@v0.7.1
|
||||||
|
|
||||||
|
- name: Format
|
||||||
|
run: make format
|
||||||
|
|
||||||
|
- name: Clippy
|
||||||
|
run: make clippy
|
||||||
|
|
||||||
|
- name: Test cargo workspace
|
||||||
|
run: make test-workspace
|
||||||
|
|
||||||
|
- name: Test docs
|
||||||
|
run: make doc
|
||||||
+12
-3
@@ -1,16 +1,25 @@
|
|||||||
/target
|
/target
|
||||||
|
/js/resolc/dist
|
||||||
|
target-llvm
|
||||||
*.dot
|
*.dot
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.zed/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/*.sol
|
/*.sol
|
||||||
/*.yul
|
/*.yul
|
||||||
/*.ll
|
/*.ll
|
||||||
/*.s
|
/*.s
|
||||||
/llvm-project
|
/llvm-*
|
||||||
/llvm18.0
|
# Allow llvm submodule directory
|
||||||
|
!/llvm
|
||||||
node_modules
|
node_modules
|
||||||
artifacts
|
artifacts
|
||||||
tmp
|
tmp
|
||||||
package-lock.json
|
|
||||||
/*.html
|
/*.html
|
||||||
/build
|
/build
|
||||||
|
soljson.js
|
||||||
|
test-results
|
||||||
|
playwright-report
|
||||||
|
.cache
|
||||||
|
emsdk
|
||||||
|
docs-tmp
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
[submodule "llvm"]
|
||||||
|
path = llvm
|
||||||
|
url = https://github.com/llvm/llvm-project.git
|
||||||
|
branch = release/18.x
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# This is no longer in .gitignore; we add it here (honored by tools like rg and fd).
|
||||||
|
llvm/
|
||||||
|
docs/
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true
|
||||||
|
}
|
||||||
+352
@@ -0,0 +1,352 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
Supported `polkadot-sdk` rev: `unstable2507`
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- The comprehensive revive compiler book documentation page: https://paritytech.github.io/revive/
|
||||||
|
- Support for solc v0.8.31.
|
||||||
|
- Support for the `clz` Yul builtin.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Instruct the LLVM backend and linker to `--relax` (may lead to smaller contract code size).
|
||||||
|
- Standard JSON mode: Don't forward EVM bytecode related output selections to solc.
|
||||||
|
- The supported `polkadot-sdk` release is `unstable2507`.
|
||||||
|
- The `INVALID` opcode and OOB memory accesses now consume all remaining gas.
|
||||||
|
- Emit the `call_evm` and `delegate_call_evm` syscalls for contract calls.
|
||||||
|
|
||||||
|
### Fixed:
|
||||||
|
- The missing `STOP` instruction at the end of `code` blocks.
|
||||||
|
- The missing bounds check in the internal sbrk implementation.
|
||||||
|
- The call gas is no longer ignored.
|
||||||
|
|
||||||
|
## v0.5.0
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
Supported `polkadot-sdk` rev: `2509.0.0`
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Support for `SELFDESTRUCT`.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Emulated EVM heap memory accesses of zero length are never out of bounds.
|
||||||
|
- Switched to newer and cheaper storage syscalls (omits reads and writes of `0` values).
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Introduced a workaround avoiding compiler crashes caused by a bug in LLVM affecting `SDIV`.
|
||||||
|
- An off-by-one bug affecting `SDIV` overflow semantics.
|
||||||
|
|
||||||
|
## v0.4.1
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
Supported `polkadot-sdk` rev: `2503.0.1`
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- The `ast` output is no longer pruned in standard JSON mode (required for foundry).
|
||||||
|
- Support `standard_json.output_selection` to also look at per file settings.
|
||||||
|
|
||||||
|
## v0.4.0
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
Supported `polkadot-sdk` rev: `2503.0.1`
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Remove the broken `--llvm-ir` mode.
|
||||||
|
- Remove the unused fallback for size optimization setting.
|
||||||
|
- Unlinked contract binaries are emitted as raw ELF objects.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Line debug information per YUL builtin and for `if` statements.
|
||||||
|
- Column numbers in debug information.
|
||||||
|
- Support for the YUL optimizer details in the standard json input definition.
|
||||||
|
- The `revive-explorer` compiler utility.
|
||||||
|
- `revive-yul`: The AST visitor interface.
|
||||||
|
- The `--link` deploy time linking mode.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- The debug info source file matches the YUL path in `--debug-output-dir`, allowing tools to display the source line.
|
||||||
|
- Incosistent type forwarding in JSON output (empty string vs. null object).
|
||||||
|
- The solc automatic import resolution.
|
||||||
|
- Compiler panic on missing libraries definition.
|
||||||
|
|
||||||
|
## v0.3.0
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
Supported `polkadot-sdk` rev: `2503.0.1`
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- llvm-context: Bugfix the SAR YUL builtin translation.
|
||||||
|
- runtime-api: Add the missing `memset` builtin.
|
||||||
|
- npm package: Bugfix the exports field defined in the `package.json`.
|
||||||
|
|
||||||
|
|
||||||
|
## v0.2.0
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
Supported `polkadot-sdk` rev: `2503.0.1`
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Removed the license printer from the `resolc` binary.
|
||||||
|
- EVM bytecode is no longer requested from solc (except in test utils) leading to less compilation work in the pipeline.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- solc-json-interface: Serializing of any custom key in the JSON input is only skipped if not provided.
|
||||||
|
- npm package resolution no longer fails with an 'ERR_PACKAGE_PATH_NOT_EXPORTED' error for packages defining exports fields in the `package.json`.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
## v0.1.0-dev.16
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
- By default, heavy size optimizations are applied.
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- The default PVM stack memory size was increased from 16kb to 32kb.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Constructors avoid storing zero sized immutable data on exit.
|
||||||
|
|
||||||
|
## v0.1.0-dev.13
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee`
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
- `--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.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
- Running `resolc` using webkit is no longer supported.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- A missing byte swap for the create2 salt value.
|
||||||
|
|
||||||
|
## v0.1.0-dev.12
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
Supported `polkadot-sdk` rev: `21f6f0705e53c15aa2b8a5706b208200447774a9`
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Per file output selection for `--standard-json` mode.
|
||||||
|
- The `ir` output selection option for `--standard-json` mode.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Improved code size: Large contracts compile to smaller code blobs when enabling aggressive size optimizations (`-Oz`).
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
## v0.1.0-dev.11
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- A bug causing incorrect loads from the emulated EVM linear memory.
|
||||||
|
- A missing integer truncate after switching to 64bit.
|
||||||
|
|
||||||
|
## v0.1.0-dev.10
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
Supported `polkadot-sdk` rev: `274a781e8ca1a9432c7ec87593bd93214abbff50`
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Support for the `coinbase` opcode.
|
||||||
|
- The resolc web JS version.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Missing the `--overwrite` flag emits an error instead of a warning.
|
||||||
|
- The `resolc` executable prints the help by default.
|
||||||
|
- Removed support for legacy EVM assembly (EVMLA) translation.
|
||||||
|
- integration: identify cached code blobs on source code to fix potential confusions.
|
||||||
|
- Setting base, include or allow paths in emscripten is now a hard error.
|
||||||
|
- Employ a heuristic to detect `address.transfer` and `address.send` calls.
|
||||||
|
If detected, the re-entrant call flag is not set and 0 deposit limit is endowed.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Solidity: Add the solc `--libraries` files to sources.
|
||||||
|
- A data race in tests.
|
||||||
|
- Fix `broken pipe` errors.
|
||||||
|
- llvm-builder: Allow warnings.
|
||||||
|
- solidity: Fix the custom compiler warning messages.
|
||||||
|
|
||||||
|
## v0.1.0-dev.9
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Syscalls with more than 6 arguments now pack them into registers.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Remove reloading of the resolc.js file (fix issue with relative path in web worker)
|
||||||
|
|
||||||
|
## v0.1.0-dev.8
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Suported contracts runtime is polkadot-sdk git version `d62a90c8c729acd98c7e9a5cab9803b8b211ffc5`.
|
||||||
|
- The minimum supported Rust version is `1.81.0`.
|
||||||
|
- Error out early instead of invoking `solc` with invalid base or include path flags.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
- Fixes input normalization in the Wasm version.
|
||||||
|
|
||||||
|
## v0.1.0-dev.7
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Implement the `GASPRICE` opcode.
|
||||||
|
- Implement the `BASEFEE` opcode.
|
||||||
|
- Implement the `GASLIMIT` opcode.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- The `GAS` opcode now returns the remaining `ref_time`.
|
||||||
|
- 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.
|
||||||
|
- 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
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
- The git commit hash slug is always displayed in the version string.
|
||||||
|
|
||||||
|
## v0.1.0-dev.6
|
||||||
|
|
||||||
|
This is a development pre-release.
|
||||||
|
|
||||||
|
# Added
|
||||||
|
|
||||||
|
- Implement the `BLOCKHASH` opcode.
|
||||||
|
- Implement delegate calls.
|
||||||
|
- Implement the `GASPRICE` opcode. Currently hard-coded to return `1`.
|
||||||
|
- The ELF shared object contract artifact is dumped into the debug output directory.
|
||||||
|
- Initial support for emitting debug info (opt in via the `-g` flag)
|
||||||
|
|
||||||
|
# Changed
|
||||||
|
|
||||||
|
- resolc now emits 64bit PolkaVM blobs, reducing contract code size and execution time.
|
||||||
|
- The RISC-V bit-manipulation target feature (`zbb`) is enabled.
|
||||||
|
|
||||||
|
# Fixed
|
||||||
|
|
||||||
|
- Compilation to Wasm (for usage in node and web browsers)
|
||||||
|
|
||||||
|
## v0.1.0-dev.5
|
||||||
|
|
||||||
|
This is development pre-release.
|
||||||
|
|
||||||
|
# Added
|
||||||
|
|
||||||
|
- Implement the `CODESIZE` and `EXTCODESIZE` opcodes.
|
||||||
|
|
||||||
|
# Changed
|
||||||
|
|
||||||
|
- Include the full revive version in the contract metadata.
|
||||||
|
|
||||||
|
# Fixed
|
||||||
|
|
||||||
|
## v0.1.0-dev-4
|
||||||
|
|
||||||
|
This is development pre-release.
|
||||||
|
|
||||||
|
# Added
|
||||||
|
|
||||||
|
- Support the `ORIGIN` opcode.
|
||||||
|
|
||||||
|
# Changed
|
||||||
|
|
||||||
|
- Update polkavm to `v0.14.0`.
|
||||||
|
- Enable the `a`, `fast-unaligned-access` and `xtheadcondmov` LLVM target features, decreasing the code size for some contracts.
|
||||||
|
|
||||||
|
# Fixed
|
||||||
Generated
+5172
-2610
File diff suppressed because it is too large
Load Diff
+58
-48
@@ -10,69 +10,79 @@ authors = [
|
|||||||
]
|
]
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/xermicus/revive"
|
repository = "https://github.com/paritytech/revive"
|
||||||
|
rust-version = "1.85.0"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
revive-benchmarks = { version = "0.1.0", path = "crates/benchmarks" }
|
|
||||||
revive-builtins = { version = "0.1.0", path = "crates/builtins" }
|
|
||||||
revive-common = { version = "0.1.0", path = "crates/common" }
|
|
||||||
revive-differential = { version = "0.1.0", path = "crates/differential" }
|
|
||||||
revive-integration = { version = "0.1.0", path = "crates/integration" }
|
|
||||||
revive-linker = { version = "0.1.0", path = "crates/linker" }
|
|
||||||
lld-sys = { version = "0.1.0", path = "crates/lld-sys" }
|
lld-sys = { version = "0.1.0", path = "crates/lld-sys" }
|
||||||
revive-llvm-context = { version = "0.1.0", path = "crates/llvm-context" }
|
resolc = { version = "0.5.0", path = "crates/resolc", default-features = false }
|
||||||
revive-runtime-api = { version = "0.1.0", path = "crates/runtime-api" }
|
revive-benchmarks = { version = "0.1.0", path = "crates/benchmarks" }
|
||||||
revive-runner = { version = "0.1.0", path = "crates/runner" }
|
revive-build-utils = { version = "0.2.0", path = "crates/build-utils" }
|
||||||
revive-solidity = { version = "0.1.0", path = "crates/solidity" }
|
revive-builtins = { version = "0.1.0", path = "crates/builtins" }
|
||||||
revive-stdlib = { version = "0.1.0", path = "crates/stdlib" }
|
revive-common = { version = "0.2.1", path = "crates/common" }
|
||||||
|
revive-differential = { version = "0.2.0", path = "crates/differential" }
|
||||||
|
revive-explorer = { version = "0.1.0", path = "crates/explore" }
|
||||||
|
revive-integration = { version = "0.3.0", path = "crates/integration" }
|
||||||
|
revive-linker = { version = "0.2.0", path = "crates/linker" }
|
||||||
|
revive-llvm-context = { version = "0.5.0", path = "crates/llvm-context" }
|
||||||
|
revive-runner = { version = "0.3.0", path = "crates/runner" }
|
||||||
|
revive-runtime-api = { version = "0.4.0", path = "crates/runtime-api" }
|
||||||
|
revive-solc-json-interface = { version = "0.4.0", path = "crates/solc-json-interface", default-features = false }
|
||||||
|
revive-stdlib = { version = "0.2.0", path = "crates/stdlib" }
|
||||||
|
revive-yul = { version = "0.4.0", path = "crates/yul" }
|
||||||
|
|
||||||
hex = "0.4"
|
hex = "0.4.3"
|
||||||
petgraph = "0.6"
|
cc = "1.2"
|
||||||
cc = "1.0"
|
libc = "0.2.172"
|
||||||
libc = "0.2"
|
tempfile = "3.23"
|
||||||
tempfile = "3.8"
|
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
semver = { version = "1.0", features = [ "serde" ] }
|
semver = { version = "1.0", features = ["serde"] }
|
||||||
itertools = "0.12"
|
itertools = "0.14"
|
||||||
serde = { version = "1.0", features = [ "derive" ] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = { version = "1.0", features = [ "arbitrary_precision" ] }
|
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
|
||||||
regex = "1.10"
|
regex = "1.12"
|
||||||
once_cell = "1.19"
|
once_cell = "1.21"
|
||||||
num = "0.4"
|
num = "0.4.3"
|
||||||
sha1 = "0.10"
|
sha1 = "0.10"
|
||||||
sha2 = "0.10"
|
|
||||||
sha3 = "0.10"
|
sha3 = "0.10"
|
||||||
md5 = "0.7"
|
thiserror = "2.0"
|
||||||
colored = "2.1"
|
which = "8.0"
|
||||||
thiserror = "1.0"
|
|
||||||
which = "5.0"
|
|
||||||
path-slash = "0.2"
|
path-slash = "0.2"
|
||||||
rayon = "1.8"
|
rayon = "1.11"
|
||||||
clap = { version = "4", default-features = false, features = ["derive"] }
|
clap = { version = "4", default-features = false, features = ["derive"] }
|
||||||
rand = "0.8"
|
polkavm-common = "0.30.0"
|
||||||
polkavm-common = "0.10"
|
polkavm-linker = "0.30.0"
|
||||||
polkavm-linker = "0.10"
|
polkavm-disassembler = "0.30.0"
|
||||||
polkavm-disassembler = "0.10"
|
polkavm = "0.30.0"
|
||||||
polkavm = "0.10"
|
alloy-primitives = { version = "1.4", features = ["serde"] }
|
||||||
alloy-primitives = { version = "0.8", features = ["serde"] }
|
alloy-sol-types = "1.4"
|
||||||
alloy-sol-types = "0.8"
|
alloy-genesis = "1.1.2"
|
||||||
alloy-genesis = "0.3"
|
alloy-serde = "1.1"
|
||||||
alloy-serde = "0.3"
|
env_logger = { version = "0.11.8", default-features = false }
|
||||||
env_logger = { version = "0.10.0", default-features = false }
|
serde_stacker = "0.1.14"
|
||||||
serde_stacker = "0.1"
|
criterion = { version = "0.7", features = ["html_reports"] }
|
||||||
criterion = { version = "0.5", features = ["html_reports"] }
|
log = { version = "0.4.28" }
|
||||||
log = { version = "0.4" }
|
git2 = { version = "0.20.2", default-features = false }
|
||||||
|
downloader = "0.2.8"
|
||||||
|
flate2 = "1.1"
|
||||||
|
fs_extra = "1.3"
|
||||||
|
num_cpus = "1"
|
||||||
|
tar = "0.4"
|
||||||
|
toml = "0.9"
|
||||||
|
assert_cmd = "2"
|
||||||
|
assert_fs = "1.1"
|
||||||
|
normpath = "1.5"
|
||||||
|
|
||||||
# polkadot-sdk and friends
|
# polkadot-sdk and friends
|
||||||
codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" }
|
codec = { version = "3.7.5", default-features = false, package = "parity-scale-codec" }
|
||||||
scale-info = { version = "2.11.1", default-features = false }
|
scale-info = { version = "2.11.6", default-features = false }
|
||||||
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "d7b575338b2c647e04fc48bfbe00ea8f492fb580" }
|
polkadot-sdk = { version = "=2507.4.0" }
|
||||||
|
|
||||||
# llvm
|
# llvm
|
||||||
[workspace.dependencies.inkwell]
|
[workspace.dependencies.inkwell]
|
||||||
version = "0.5"
|
version = "0.6.0"
|
||||||
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"
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
FROM rust:1.85.0 AS llvm-builder
|
||||||
|
WORKDIR /opt/revive
|
||||||
|
|
||||||
|
RUN apt update && \
|
||||||
|
apt upgrade -y && \
|
||||||
|
apt install -y cmake ninja-build curl git libssl-dev pkg-config clang lld musl
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN make install-llvm-builder
|
||||||
|
RUN revive-llvm --target-env musl clone
|
||||||
|
RUN revive-llvm --target-env musl build --llvm-projects lld --llvm-projects clang
|
||||||
|
|
||||||
|
FROM messense/rust-musl-cross@sha256:c0154e992adb791c3b848dd008939d19862549204f8cb26f5ca7a00f629e6067 AS resolc-builder
|
||||||
|
WORKDIR /opt/revive
|
||||||
|
|
||||||
|
RUN apt update && \
|
||||||
|
apt upgrade -y && \
|
||||||
|
apt install -y pkg-config
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
COPY --from=llvm-builder /opt/revive/target-llvm /opt/revive/target-llvm
|
||||||
|
|
||||||
|
ENV LLVM_SYS_181_PREFIX=/opt/revive/target-llvm/musl/target-final
|
||||||
|
RUN make install-bin
|
||||||
|
|
||||||
|
FROM alpine:latest
|
||||||
|
ADD https://github.com/ethereum/solidity/releases/download/v0.8.28/solc-static-linux /usr/bin/solc
|
||||||
|
COPY --from=resolc-builder /root/.cargo/bin/resolc /usr/bin/resolc
|
||||||
|
|
||||||
|
RUN apk add --no-cache libc6-compat
|
||||||
|
RUN chmod +x /usr/bin/solc
|
||||||
@@ -1,30 +1,101 @@
|
|||||||
.PHONY: install format test test-solidity test-cli test-integration test-workspace clean docs docs-build
|
.PHONY: \
|
||||||
|
install \
|
||||||
|
install-bin \
|
||||||
|
install-npm \
|
||||||
|
install-wasm \
|
||||||
|
install-llvm-builder \
|
||||||
|
install-llvm \
|
||||||
|
install-revive-runner \
|
||||||
|
install-revive-explorer \
|
||||||
|
format \
|
||||||
|
clippy \
|
||||||
|
doc \
|
||||||
|
book \
|
||||||
|
machete \
|
||||||
|
test \
|
||||||
|
test-integration \
|
||||||
|
test-resolc \
|
||||||
|
test-yul \
|
||||||
|
test-workspace \
|
||||||
|
test-wasm \
|
||||||
|
test-llvm-builder \
|
||||||
|
test-book \
|
||||||
|
bench \
|
||||||
|
bench-pvm \
|
||||||
|
bench-evm \
|
||||||
|
bench-resolc \
|
||||||
|
bench-yul \
|
||||||
|
clean
|
||||||
|
|
||||||
install: install-bin install-npm
|
install: install-bin install-npm
|
||||||
|
|
||||||
install-bin:
|
install-bin:
|
||||||
cargo install --path crates/solidity
|
cargo install --force --locked --path crates/resolc
|
||||||
|
|
||||||
install-npm:
|
install-npm:
|
||||||
npm install && npm fund
|
npm install && npm fund
|
||||||
|
|
||||||
|
install-wasm: install-npm
|
||||||
|
cargo build --target wasm32-unknown-emscripten -p resolc --release --no-default-features
|
||||||
|
npm run build:package
|
||||||
|
|
||||||
|
install-llvm-builder:
|
||||||
|
cargo install --force --locked --path crates/llvm-builder
|
||||||
|
|
||||||
|
install-llvm: install-llvm-builder
|
||||||
|
git submodule update --init --recursive --depth 1
|
||||||
|
revive-llvm build --llvm-projects lld --llvm-projects clang
|
||||||
|
|
||||||
|
install-revive-runner:
|
||||||
|
cargo install --locked --force --path crates/runner --no-default-features
|
||||||
|
|
||||||
|
install-revive-explorer:
|
||||||
|
cargo install --locked --force --path crates/explorer --no-default-features
|
||||||
|
|
||||||
format:
|
format:
|
||||||
cargo fmt --all --check
|
cargo fmt --all --check
|
||||||
|
|
||||||
test: format clippy test-cli test-workspace
|
clippy:
|
||||||
cargo test --workspace
|
cargo clippy --all-features --workspace --tests --benches -- --deny warnings
|
||||||
|
|
||||||
|
doc:
|
||||||
|
cargo doc --all-features --workspace --document-private-items --no-deps
|
||||||
|
|
||||||
|
book: test-book
|
||||||
|
mdbook serve book --open
|
||||||
|
|
||||||
|
machete:
|
||||||
|
cargo install cargo-machete
|
||||||
|
cargo machete
|
||||||
|
|
||||||
|
test: format clippy machete test-workspace install-revive-runner install-revive-explorer doc test-book
|
||||||
|
|
||||||
test-integration: install-bin
|
test-integration: install-bin
|
||||||
cargo test --package revive-integration
|
cargo test --package revive-integration
|
||||||
|
|
||||||
test-solidity: install
|
test-resolc: install
|
||||||
cargo test --package revive-solidity
|
cargo test --package resolc --all-targets
|
||||||
|
|
||||||
|
test-yul:
|
||||||
|
cargo test --package revive-yul --all-targets
|
||||||
|
|
||||||
test-workspace: install
|
test-workspace: install
|
||||||
cargo test --workspace
|
cargo test --workspace --all-targets --exclude revive-llvm-builder
|
||||||
|
|
||||||
test-cli: install
|
test-wasm: install-wasm
|
||||||
npm run test:cli
|
npm run test:wasm
|
||||||
|
|
||||||
|
test-llvm-builder:
|
||||||
|
@echo "warning: the llvm-builder tests will take many hours"
|
||||||
|
cargo test --package revive-llvm-builder -- --test-threads=1
|
||||||
|
|
||||||
|
test-book:
|
||||||
|
cargo install mdbook --version 0.5.1 --locked
|
||||||
|
mdbook test book
|
||||||
|
|
||||||
|
bench: install-bin
|
||||||
|
cargo criterion --all --all-features --message-format=json \
|
||||||
|
| criterion-table > crates/benchmarks/BENCHMARKS.md
|
||||||
|
|
||||||
bench-pvm: install-bin
|
bench-pvm: install-bin
|
||||||
cargo criterion --bench execute --features bench-pvm-interpreter --message-format=json \
|
cargo criterion --bench execute --features bench-pvm-interpreter --message-format=json \
|
||||||
@@ -34,22 +105,21 @@ bench-evm: install-bin
|
|||||||
cargo criterion --bench execute --features bench-evm --message-format=json \
|
cargo criterion --bench execute --features bench-evm --message-format=json \
|
||||||
| criterion-table > crates/benchmarks/EVM.md
|
| criterion-table > crates/benchmarks/EVM.md
|
||||||
|
|
||||||
bench: install-bin
|
bench-resolc: test-resolc
|
||||||
cargo criterion --all --all-features --message-format=json \
|
cargo criterion --package resolc --bench compile --message-format=json \
|
||||||
| criterion-table > crates/benchmarks/BENCHMARKS.md
|
| criterion-table > crates/resolc/BENCHMARKS_M4PRO.md
|
||||||
|
|
||||||
clippy:
|
bench-yul: test-yul
|
||||||
cargo clippy --all-features --workspace --tests --benches
|
cargo criterion --package revive-yul --bench parse --message-format=json \
|
||||||
|
| criterion-table > crates/yul/BENCHMARKS_PARSE_M4PRO.md
|
||||||
docs: docs-build
|
cargo criterion --package revive-yul --bench lower --message-format=json \
|
||||||
mdbook serve --open docs/
|
| criterion-table > crates/yul/BENCHMARKS_LOWER_M4PRO.md
|
||||||
|
|
||||||
docs-build:
|
|
||||||
mdbook test docs/ && mdbook build docs/
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
cargo clean ; \
|
cargo clean ; \
|
||||||
|
revive-llvm clean ; \
|
||||||
rm -rf node_modules ; \
|
rm -rf node_modules ; \
|
||||||
rm -rf crates/solidity/src/tests/cli-tests/artifacts ; \
|
rm -rf crates/resolc/src/tests/cli/artifacts ; \
|
||||||
cargo uninstall revive-solidity ; \
|
cargo uninstall resolc ; \
|
||||||
rm -f package-lock.json
|
cargo uninstall revive-llvm-builder ;
|
||||||
|
mdbook clean book
|
||||||
|
|||||||
@@ -1,38 +1,110 @@
|
|||||||

|

|
||||||
|
[](https://paritytech.github.io/revive/)
|
||||||
|
|
||||||
# revive
|
# revive
|
||||||
|
|
||||||
YUL and EVM assembly recompiler to LLVM, targetting RISC-V on [PolkaVM](https://github.com/koute/polkavm).
|
Yul recompiler to LLVM, targetting RISC-V on [PolkaVM](https://github.com/koute/polkavm).
|
||||||
|
|
||||||
[Frontend](https://github.com/matter-labs/era-compiler-solidity) and [code generator](https://github.com/matter-labs/era-compiler-llvm-context) are based of ZKSync `zksolc`.
|
Check the [docs](https://paritytech.github.io/revive/) or visit [contracts.polkadot.io](https://docs.polkadot.com/develop/smart-contracts/) to learn more about `revive` and contracts on Polkadot!
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
This is experimental software in active development and not ready just yet for production usage.
|
This is experimental software in active development and not ready just yet for production usage. Please do report any compiler related issues or missing features that are [not yet known to us](https://contracts.polkadot.io/known_issues/) here.
|
||||||
|
|
||||||
Discussion around the development is hosted on the [Polkadot Forum](https://forum.polkadot.network/t/contracts-update-solidity-on-polkavm/6949#a-new-solidity-compiler-1).
|
Discussion around the development is hosted on the [Polkadot Forum](https://forum.polkadot.network/t/contracts-update-solidity-on-polkavm/6949#a-new-solidity-compiler-1).
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
`resolc` depends on the [solc](https://github.com/ethereum/solidity) binary installed on your system.
|
Building Solidity contracts for PolkaVM requires installing the following two compilers:
|
||||||
|
- `resolc`: The revive Solidity compiler Yul frontend and PolkaVM code generator (provided by this repository).
|
||||||
|
- `solc`: The [Ethereum Solidity reference compiler](https://github.com/ethereum/solidity/) implemenation.`resolc` uses `solc` during the compilation process, please refer to the [Ethereum Solidity documentation](https://docs.soliditylang.org/en/latest/installing-solidity.html) for installation instructions.
|
||||||
|
|
||||||
|
### `resolc` binary releases
|
||||||
|
|
||||||
|
`resolc` is distributed as a standalone binary (with `solc` as the only external dependency). Please download one of our [binary releases](https://github.com/paritytech/revive/releases) for your target platform and mind the platform specific instructions below.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>MacOS users</summary>
|
||||||
|
|
||||||
|
> **MacOS** users need to clear the `downloaded` attribute from the binary and set the executable flag.
|
||||||
|
> ```sh
|
||||||
|
> xattr -rc resolc-universal-apple-darwin
|
||||||
|
> chmod +x resolc-universal-apple-darwin
|
||||||
|
> ```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Linux users</summary>
|
||||||
|
|
||||||
|
> **Linux** users need to set the executable flag.
|
||||||
|
> ```sh
|
||||||
|
> chmod +x resolc-x86_64-unknown-linux-musl
|
||||||
|
> ```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
### `resolc` NPM package
|
||||||
|
|
||||||
|
We distribute the revive compiler as [node.js module](https://www.npmjs.com/package/@parity/resolc) and [hardhat plugin](https://www.npmjs.com/package/@parity/hardhat-polkadot-resolc).
|
||||||
|
|
||||||
|
Note: The `solc` dependency is bundled via NPM packaging and defaults to the latest supported version.
|
||||||
|
|
||||||
|
## Building from source
|
||||||
|
|
||||||
|
Building revive requires a [stable Rust installation](https://rustup.rs/) and a C++ toolchain for building [LLVM](https://github.com/llvm/llvm-project) on your system.
|
||||||
|
|
||||||
|
### LLVM
|
||||||
|
|
||||||
|
`revive` depends on a custom build of LLVM `v18.1.8` with the RISC-V _embedded_ target, including the `compiler-rt` builtins. You can either download a build from our releases (recommended for older hardware) or build it from source.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Download from our LLVM releases</summary>
|
||||||
|
|
||||||
|
Download the [latest LLVM build](https://github.com/paritytech/revive/releases?q=LLVM+binaries+release&expanded=true) from our releases.
|
||||||
|
|
||||||
|
> **MacOS** users need to clear the `downloaded` attribute from all binaries after extracting the archive:
|
||||||
|
> ```sh
|
||||||
|
> xattr -rc </path/to/the/extracted/archive>/target-llvm/gnu/target-final/bin/*
|
||||||
|
> ```
|
||||||
|
|
||||||
|
After extracting the archive, point `$LLVM_SYS_181_PREFIX` to it:
|
||||||
|
```sh
|
||||||
|
export LLVM_SYS_181_PREFIX=</path/to/the/extracted/archive>/target-llvm/gnu/target-final
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Building from source</summary>
|
||||||
|
|
||||||
|
The `Makefile` provides a shortcut target to obtain a compatible LLVM build, using the provided [revive-llvm](crates/llvm-builder/README.md) utility. Once installed, point `$LLVM_SYS_181_PREFIX` to the installation afterwards:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make install-llvm
|
||||||
|
export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### The `resolc` Solidity frontend
|
||||||
|
|
||||||
|
To build the `resolc` Solidity frontend executable, make sure you have obtained a compatible LLVM build and did export the `LLVM_SYS_181_PREFIX` environment variable pointing to it (see [above](#LLVM)).
|
||||||
|
|
||||||
To install the `resolc` Solidity frontend executable:
|
To install the `resolc` Solidity frontend executable:
|
||||||
|
|
||||||
```bash
|
```sh
|
||||||
bash build-llvm.sh
|
|
||||||
export PATH=${PWD}/llvm18.0/bin:$PATH
|
|
||||||
make install-bin
|
make install-bin
|
||||||
resolc --version
|
resolc --version
|
||||||
```
|
```
|
||||||
|
|
||||||
### LLVM
|
## Development
|
||||||
|
|
||||||
`revive` requires a build of LLVM 18.1.4 or later including `compiler-rt`. Use the provided [build-llvm.sh](build-llvm.sh) build script to compile a compatible LLVM build locally in `$PWD/llvm18.0` (don't forget to add that to `$PATH` afterwards).
|
Please consult the [Developer Guide](https://paritytech.github.io/revive/developer_guide/contributing.html) to learn more about how contribute to the project.
|
||||||
|
|
||||||
### Development
|
# Acknowledgements
|
||||||
|
|
||||||
Please consult the [Makefile](Makefile) targets to learn how to run tests and benchmarks.
|
The revive compiler project, after some early experiments with EVM bytecode translations, decided to fork the `era-compiler` framework.
|
||||||
Ensure that your branch passes `make test` locally when submitting a pull request.
|
[Frontend](https://github.com/matter-labs/era-compiler-solidity), [code generator](https://github.com/matter-labs/era-compiler-llvm-context) and some supporting libraries are based of ZKSync `zksolc`. I'd like to express my gratitude and thank the original authors for providing a useable code base under a generous license.
|
||||||
|
|
||||||
## Design overview
|
|
||||||
`revive` uses [solc](https://github.com/ethereum/solidity/), the Ethereum Solidity compiler, as the [Solidity frontend](crates/solidity/src/lib.rs) to process smart contracts written in Solidity. The YUL IR code (or legacy EVM assembly as a fallback for older `solc` versions) emitted by `solc` is then translated to LLVM IR, targetting [Polkadots `revive` pallet](https://docs.rs/pallet-revive/latest/pallet_revive/trait.SyscallDoc.html).
|
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
# Release checklist
|
||||||
|
|
||||||
|
Prior to the first stable release we neither have formal release processes nor do we follow a fixed release schedule.
|
||||||
|
|
||||||
|
To create a new pre-release:
|
||||||
|
|
||||||
|
1. Create a release PR which, if necessary:
|
||||||
|
- Updates the versions in the workspace `Cargo.toml`
|
||||||
|
- Updates the version in each crate `Cargo.toml`
|
||||||
|
- Updates the version of the NPM package in `js/resolc/package.json`
|
||||||
|
- Updates the `CHANGELOG.md` to reflect all observable changes
|
||||||
|
2. If the CI passes, merge the release PR.
|
||||||
|
3. Push a `vX.Y.Z` tag that has the same version as in `Cargo.toml`
|
||||||
|
4. The release workflow will attempt to build and publish a new pre-release if the latest tag does match the cargo package version.
|
||||||
|
5. Wait for the `Release` workflow to finish. It should create the pre-release with the same name.
|
||||||
|
6. Check that pre-release was created on the [Releases page](https://github.com/paritytech/revive/releases) with all artifacts.
|
||||||
|
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
|
||||||
|
|
||||||
|
# `resolc` NPM package release
|
||||||
|
|
||||||
|
Will happen automatically.
|
||||||
|
|
||||||
|
# LLVM release
|
||||||
|
|
||||||
|
To create a new LLVM release, run "Release LLVM" workflow. Use current LLVM version as parameter, e.g. `18.1.8`.
|
||||||
|
Version suffix will be resolved automatically.
|
||||||
|
The workflows will create new GitHub release, and upload LLVM binaries.
|
||||||
|
Next release of resolc will use newly created binaries.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/SECURITY.md
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
book
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
[book]
|
||||||
|
title = "revive compiler book"
|
||||||
|
authors = ["xermicus"]
|
||||||
|
language = "en"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
build-dir = "../docs"
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Summary
|
||||||
|
|
||||||
|
- [Welcome](./welcome.md)
|
||||||
|
- [`resolc` user guide](./user_guide.md)
|
||||||
|
- [Installation](./user_guide/installation.md)
|
||||||
|
- [Command Line Interface](./user_guide/cli.md)
|
||||||
|
- [JS NPM package](./user_guide/js.md)
|
||||||
|
- [Tooling integration](./user_guide/tooling.md)
|
||||||
|
- [Standard JSON interface](./user_guide/std_json.md)
|
||||||
|
- [Differences to EVM](./user_guide/differences.md)
|
||||||
|
- [Rust contract libraries](./user_guide/rust_libraries.md)
|
||||||
|
- [`revive-runner` sandbox](./revive_runner.md)
|
||||||
|
- [Developer Guide](./developer_guide.md)
|
||||||
|
- [Contributor guide](./developer_guide/contributing.md)
|
||||||
|
- [Compiler architecture](./developer_guide/architecture.md)
|
||||||
|
- [PVM and the pallet-revive runtime target](./developer_guide/target.md)
|
||||||
|
- [Testing strategy](./developer_guide/testing.md)
|
||||||
|
- [Cross compilation](./developer_guide/cross_compilation.md)
|
||||||
|
- [FAQ](./faq.md)
|
||||||
|
- [Roadmap and Vision](./roadmap.md)
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Developer guide
|
||||||
|
|
||||||
|
This chapter covers internal aspects of the compiler and helps contributors getting started with the `revive` codebase.
|
||||||
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# Compiler architecture and internals
|
||||||
|
|
||||||
|
`revive` relies on `solc`, the [Ethereum Solidity compiler](https://github.com/argotorg/solidity), as the Solidity frontend to process smart contracts written in Solidity. [LLVM](https://github.com/llvm/llvm-project), a popular and powerful compiler framework, is used as the compiler backend and does the heavy lifting in terms of optimizitations and RISC-V code generation.
|
||||||
|
|
||||||
|
`revive` mainly takes care of lowering the Yul intermediate representation (IR) produced by `solc` to LLVM IR. This approach provides a good balance between maintaining a high level of Ethereum compatibility, good contract performance and feasible engineering efforts.
|
||||||
|
|
||||||
|
## `resolc`
|
||||||
|
|
||||||
|
`resolc` is the overarching compiler driver library and binary.
|
||||||
|
|
||||||
|
When compiling a Solidity source file with `resolc`, the following steps happen under the hood:
|
||||||
|
1. `solc` is used to lower the Solidity source code into [YUL intermediate representation](https://docs.soliditylang.org/en/latest/yul.html).
|
||||||
|
2. `revive` lowers the YUL IR into LLVM IR.
|
||||||
|
3. LLVM optimizes the code and emits a RISC-V ELF shared object (through LLD).
|
||||||
|
4. The [PolkaVM](https://github.com/paritytech/polkavm) linker finally links the ELF shared object into a PolkaVM blob.
|
||||||
|
|
||||||
|
This compilation process can be visualized as follows:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Reproducible contract builds
|
||||||
|
|
||||||
|
Because on-chain contract code is identified via its code blob hash, it is crucial to maintain reproducible contract builds. A given compiler version must reproduce the contract build _exactly_ on every target platform `resolc` supports via the official binary releases.
|
||||||
|
|
||||||
|
To ensure this, we employ the following measures:
|
||||||
|
- The code generation must be fully deterministic. For example iterating over standard `HashMap` invalidates this due to its internal state, making it an invalid operation in `revive`. To circumvent that, a `BTreeMap` can be used instead.
|
||||||
|
- We release fully statically linked `resolc` binaries. This prevents dynamic linking of potentially differentiating libraries.
|
||||||
|
- The only non-bundled dependency is the `solc` compiler. This is considered fine because the same properties apply to `solc`.
|
||||||
|
|
||||||
|
## The `revive` compiler libraries
|
||||||
|
|
||||||
|
The main compiler logic is implemented in the `revive-yul` and `revive-llvm-context` crates.
|
||||||
|
|
||||||
|
The Yul library implements a lexer and parser and lowers the resulting tree into LLVM IR. It does so by emitting LL using the LLVM builder and our own `revive-llvm-context` compiler context crate. The revive LLVM context crate encapsulates code generation logic (decoupled from the parser).
|
||||||
|
|
||||||
|
The Yul library also implements a simple [visitor](https://en.wikipedia.org/wiki/Visitor_pattern) interface (see [visitor.rs](https://github.com/paritytech/revive/blob/main/crates/yul/src/visitor.rs)). If you want to work with the AST, it is strongly recommended to implement visitors. The LLVM code generation is implemented using a dedicated trait for historical reasons only.
|
||||||
|
|
||||||
|
## EVM heap memory
|
||||||
|
|
||||||
|
PVM doesn't offer a similar API. Hence the emitted contract code emulates the linear EVM heap memory using a static byte buffer. Data inside this byte buffer is kept big endian for EVM compatibility reasons (unaligned access is allowed and makes optimizing this non-trivial).
|
||||||
|
|
||||||
|
Unlike with the EVM, where heap memory usage is gas metered, our heap size is static (the size is user controllable via a setting flag). The compiler emits bound checks to prevent overflows.
|
||||||
|
|
||||||
|
## The LLVM dependency
|
||||||
|
|
||||||
|
LLVM is a special non Rust dependency. We interface its builder interface via the [inkwell](https://crates.io/crates/inkwell) wrapper crate.
|
||||||
|
|
||||||
|
We use upstream LLVM, but release and use our custom builds. We require the compiler builtins specifically built for the PVM `rv64emacb` target and always leave assertions on. Furthermore, we need cross builds because `resolc` itself targets emscripten and musl. The [revive-llvm-builer](https://crates.io/crates/revive-llvm-builder) functions as a cross-platform build script and is used to build and release the LLVM dependency.
|
||||||
|
|
||||||
|
We also maintain the [lld-sys crate](https://crates.io/crates/lld-sys) for interfacing with `LLD`. The LLVM linker is used during the compilation process, but we don't want to distribute another binary.
|
||||||
|
|
||||||
|
|
||||||
|
## Custom optimizations
|
||||||
|
|
||||||
|
At the moment, no significant custom optimizations are implemented. Thus, we are missing some optimization opportunities that neither `solc` nor LLVM can realize (due to their lack of domain specific knowledge about the semantics of our target environment). Furthermore, `solc` optimizes for EVM gas and a target machine orthogonal to our target (BE 256-bit stack machine EVM vs. 64-bit LE RISC architecture PVM). We have started working on an additional IR layer between Yul and LLVM to capture missed optimization opportunities, though.
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# Contributor guide
|
||||||
|
|
||||||
|
The `revive` compiler is an open source software project and we gladly accept quality contributions from anyone!
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
A quick reference on how to build the Solidity compiler is maintained in the project's [README.md](https://github.com/paritytech/revive?tab=readme-ov-file#building-from-source).
|
||||||
|
|
||||||
|
### Using the `Makefile`
|
||||||
|
|
||||||
|
The [Makefile](https://github.com/paritytech/revive/blob/main/Makefile) comprehensively encapsulates all development aspects of this codebase. It is kept concise and readable. Please read and use it! You'll learn for example:
|
||||||
|
|
||||||
|
- How to build and install a `resolc` development version.
|
||||||
|
- How to run tests and benchmarks.
|
||||||
|
- How to cross-compile `resolc`.
|
||||||
|
|
||||||
|
As a general rule-of-thumb: If `make test` runs fine locally, chances for green CI pipelines are good.
|
||||||
|
|
||||||
|
### Codebase organization
|
||||||
|
|
||||||
|
For the most parts, `revive` is a rather standard Rust workspace codebase. There are some non-Rust dependencies, which sometimes complicates things a little bit.
|
||||||
|
|
||||||
|
#### The `crates/` dir
|
||||||
|
|
||||||
|
All Rust crates live under the `crates/` directory. The workspace automatically considers any crate found therein. If you need to add a new create, please implement it there.
|
||||||
|
|
||||||
|
Compiler library crates should be named with the `revive-` prefix. The crate location doesn't need the prefix.
|
||||||
|
|
||||||
|
#### Dependencies
|
||||||
|
|
||||||
|
Dependencies should be added as workspace dependencies. Try to avoid pinning dependencies whenever possible. If possible, add dev dependencies as `dev-dependencies` only.
|
||||||
|
|
||||||
|
Please do always include the `Cargo.lock` dependency lock file with your PR. Please don't run `cargo update` together with other changes (it is preferred to update the lock file in a dedicated dependency update PR).
|
||||||
|
|
||||||
|
## Contribution rules
|
||||||
|
|
||||||
|
1. Changes must be submitted via a pull request (PR) to the github upstream repository.
|
||||||
|
2. Ensure that your branch passes `make test` locally when submitting a pull request.
|
||||||
|
3. A PR must not be merged until CI fully passes. Exceptions can be made (for example to fix CI issues itself).
|
||||||
|
4. No force pushes to the `main` branch and open PR branches.
|
||||||
|
5. Maintainers can request changes or deny contributions at their own discretion.
|
||||||
|
|
||||||
|
## Style guide
|
||||||
|
|
||||||
|
We require the official Rust formatter and clippy linter. In addition to that, please also consider the following best-effort aspects:
|
||||||
|
|
||||||
|
- Avoid [magic numbers](https://en.wikipedia.org/wiki/Magic_number_(programming)) and strings. Instead, add them as module constants.
|
||||||
|
- Avoid abbreviated variable and function names. Always provide meaningful and readable symbols.
|
||||||
|
- Don't write macros and don't use third party macros for things that can easily be expressed in few lines of code or outlined into functions.
|
||||||
|
- Avoid import aliasing. Please use the parent or fully qualified path for conflicting symbols.
|
||||||
|
- Any inline comments must provide additional semantic meaning, explain counter-intuitive behavior or highlight non-obvious design decisions. In other words, try to make the code expressive enough to a degree it doesn't need comments expressing the same thing again in the English language. Delete such comments if your AI assistant generated them.
|
||||||
|
- Public items must have a meaningful doc comment.
|
||||||
|
- Provide meaningful panic messages to `.expect()` or just use `.unwrap()`.
|
||||||
|
|
||||||
|
## AI policy
|
||||||
|
|
||||||
|
Contributors may use whatever AI assistance tools they wish to whatever degree they wish in the process of creating their contribution, __given they acknowledge the following__:
|
||||||
|
|
||||||
|
_Project maintainers may reject any contribution (or portions of it) if the contribution shows signs of problematic involvement of generative AI_.
|
||||||
|
|
||||||
|
Judgement of "problematic involvement" lies at the sole discretion of project maintainers. No proof (whether a contribution was in fact AI generated or not) is required. Rationale:
|
||||||
|
|
||||||
|
- No one enjoys reading soulless and uncanny LLM slop. Please review and fix any AI slop yourself prior to submitting a PR.
|
||||||
|
- A Solidity compiler is security sensitive software. Even miniscule mistakes can ultimately lead to loss of funds. AI models are inherently stochastic. They regurarly fail to capture important nuances or produce straight hallucinations. Code that was "blindly" generated has no home here.
|
||||||
|
- `revive` is a large codebase. Generative AI assistants may not have enough "context window" to sufficiently capture correctness, consistency and style aspects of the codebase. We'd like to keep this codebase maintainable _by humans_ for the forseeable future.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Cross compilation
|
||||||
|
|
||||||
|
We cross-compile the `resolc.js` frontend executable to Wasm for running it in a Node.js or browser environment.
|
||||||
|
|
||||||
|
The [musl](https://www.musl-libc.org/) target is used to obtain statically linked ELF binaries for Linux.
|
||||||
|
|
||||||
|
## Wasm via emscripten
|
||||||
|
|
||||||
|
The `REVIVE_LLVM_TARGET_PREFIX` environment variable is used to control the target environment LLVM dependency. This requires a compatible LLVM build, obtainable via the `revive-llvm` build script. Example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Build the host LLVM dependency with PolkaVM target support
|
||||||
|
make install-llvm
|
||||||
|
export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final
|
||||||
|
|
||||||
|
# Build the target LLVM dependency with PolkaVM target support
|
||||||
|
revive-llvm emsdk
|
||||||
|
source emsdk/emsdk_env.sh
|
||||||
|
revive-llvm --target-env emscripten build --llvm-projects lld
|
||||||
|
export REVIVE_LLVM_TARGET_PREFIX=${PWD}/target-llvm/emscripten/target-final
|
||||||
|
|
||||||
|
# Build the resolc frontend executable
|
||||||
|
make install-wasm
|
||||||
|
make test-wasm
|
||||||
|
```
|
||||||
|
|
||||||
|
## musl libc
|
||||||
|
|
||||||
|
[rust-musl-cross](https://github.com/rust-cross/rust-musl-cross) is a straightforward way to cross compile Rust to musl. The [Dockerfile](https://github.com/paritytech/revive/blob/main/Dockerfile) is an executable example of how to do that.
|
||||||
|
|
||||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 290 KiB |
@@ -0,0 +1,29 @@
|
|||||||
|
# PVM and the pallet-revive runtime target
|
||||||
|
|
||||||
|
The `revive` compiler targets [PolkaVM (PVM)](https://github.com/paritytech/polkavm) via [pallet-revive](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/revive) on Polkadot.
|
||||||
|
|
||||||
|
## Target CPU configuration
|
||||||
|
|
||||||
|
The exact target CPU configuration can be found [here](https://github.com/paritytech/revive/blob/8cd10a613625428956eb33c39c9022a91bfbf103/crates/llvm-context/src/target_machine/mod.rs#L22-L32).
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> The PVM linker requires fully relocatable ELF objects.
|
||||||
|
|
||||||
|
## Why PVM
|
||||||
|
|
||||||
|
PVM is a RISC-V based VM designed to overcome the flaws of [WebAssebmly (Wasm)](https://webassembly.org/). Wasm was believed to be a more efficient successor to the rather slow EVM. However, Wasm is far from an ideal target for smart contracts as some of its design decisions are unfavorable for short-lived workloads. The main problem is on-chain Wasm bytecode compilation or interpretation overhead. Prior benchmarks consistently ignoring this overhead seeded the blockchain industry with flawed assumptions: _Only when ignoring the startup overhead_ Wasm is much faster than the slow computing EVM. In practice however, gains are nullified entirely and Wasm loses completely even against very slow VMs like the EVM. Executing Wasm contracts is in fact so inefficient that typical contract workloads are _orders of magnitude_ more expensive than the equivalent EVM variant.
|
||||||
|
|
||||||
|
On the other hand, since RISC-V is similar to CPUs found in validator hardware (x86 and ARM), bytecode translation mostly boils down to a linear mapping from one instruction to another. The _embedded_ ISA specification reduces the number of general purpose registers, in turn removing the need for expensive register allocation. This guarantees single-pass `O(n)` JIT compilation of contract bytecode. The close proximity of PVM bytecode with actual validator CPU bytecode effectively allows to move all expensive compilation workload off-chain. Benchmarks ([1](https://hackmd.io/@XXX9CM1uSSCWVNFRYaSB5g/HJarTUhJA#Results-execute), [2](https://github.com/paritytech/polkavm/blob/master/BENCHMARKS.md)) show that with the PVM JIT, sandboxed PVM code executes at around half the speed of native code, which falls into the same ballpark of the state-of-the-art `wasmtime` Wasm implementation (while EVM sits somewhere around 1/10 to less than 1/100 of native speed). However, the PVM JIT compiler only uses a fraction of the time `wasmtime` requires to compile the code.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> The PVM JIT isn't available yet in `pallet-revive`. At the time of writing, the contract code is interpreted, which is orders of magnitude slower than the JIT.
|
||||||
|
|
||||||
|
## Host environment: `pallet-revive`
|
||||||
|
|
||||||
|
The `revive` compiler targets the [`pallet-revive` runtime environment](https://docs.rs/pallet-revive/).
|
||||||
|
|
||||||
|
`pallet-revive` exposes a [syscall like interface](https://docs.rs/pallet-revive/latest/pallet_revive/trait.SyscallDoc.html) for contract interactions with the host environment. This is provided by the [revive-runtime-api](https://crates.io/crates/revive-runtime-api) library.
|
||||||
|
|
||||||
|
After the initial launch on the Polkadot Asset Hub blockchain, the runtime API is considered stable and backwards compatible indefinitively.
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# Testing strategy
|
||||||
|
|
||||||
|
Contributors are encouraged to implement some appropriate unit and integration tests together with any bug fixes or new feature implementations. However, when it comes to testing the code generation logic, our testing strategy goes way beyond simple unit and integration tests. This chapter explains how the `revive` compiler implementation is tested for correctness and how we define correctness.
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
>
|
||||||
|
> Running the integration tests require the `evm` tool from `go-ethereum` in your `$PATH`.
|
||||||
|
>
|
||||||
|
> Either install it using your package manager or to build it from source:
|
||||||
|
> ```bash
|
||||||
|
> git clone https://github.com/ethereum/go-ethereum/
|
||||||
|
> cd go-ethereum
|
||||||
|
> make all
|
||||||
|
> export PATH=/path/to/go-ethereum/build/bin/:$PATH
|
||||||
|
> ```
|
||||||
|
|
||||||
|
## Bug compatibility with Ethereum Solidity
|
||||||
|
|
||||||
|
As a Solidity compiler, we aim to preserve contract code semantics as close as possible to Solidity compiled to EVM with the `solc` reference implementation. As highlighted in the user guide, due to the underlying target difference, this isn't always possible. However, wherever it is possible, we follow the philosophy of [**bug compatibility**](https://en.wikipedia.org/wiki/Bug_compatibility) with the Ethereum contracts stack.
|
||||||
|
|
||||||
|
## Differential integration tests
|
||||||
|
|
||||||
|
A high level of bug compatibility with Ethereum is ensured through [**differential testing**](https://en.wikipedia.org/wiki/Differential_testing) with the Ethereum `solc` and EVM contracts stack. The [revive-integration](https://crates.io/crates/revive-integration) library is the central integration test utility, providing a set of Solidity integration test cases. Further, it implements differential tests against the reference implementation by combining the [revive-runner](https://crates.io/crates/revive-runner) sandbox, the [go-ethereum EVM tool](https://github.com/ethereum/go-ethereum/tree/master/cmd/evm) and the [revive-differential](https://crates.io/crates/revive-differential).
|
||||||
|
|
||||||
|
The `revive-runner` library provides a [**declarative**](https://en.wikipedia.org/wiki/Declarative_programming) test [specification format](https://github.com/paritytech/revive/blob/main/crates/runner/src/specs.rs). This vastly simplifies writing differential test cases and removes a lot of room for errors in test logic. Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"differential": true,
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"Instantiate": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "Bitwise"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Call": {
|
||||||
|
"dest": {
|
||||||
|
"Instantiated": 0
|
||||||
|
},
|
||||||
|
"data": "3fa4f245"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Above example instantiates the `Bitwise` contract and calls it with some defined calldata. The `revive-runner` library implements a helper wrapper to execute test specs on the go-ethereum standalone `evm` tool. This allows the `revive-runner` to execute specs against the EVM and the `pallet-revive` runtime. Key to differential testing is setting `"differential": true`, resulting in the following:
|
||||||
|
|
||||||
|
1. The `Bitwise` contract is compiled to EVM and PVM code.
|
||||||
|
2. The runner executes the defined `actions` on the EVM and collects all state changes (storage, balance) and execution results.
|
||||||
|
3. The runner executes each action on the PVM. Observed state changes _after each step_ as well as the final execution result is asserted to match the EVM counterparts __exactly__.
|
||||||
|
|
||||||
|
__Note how we never defined any expected outcome manually.__ Instead, we simply observe and collect the data defining the "correct" outcome.
|
||||||
|
|
||||||
|
Differential testing in combination with declarative test specifications proved to be simple, yet very effective, in ensuring expected Ethereum Solidity semantics on `pallet-revive`.
|
||||||
|
|
||||||
|
## The differential testing utility
|
||||||
|
|
||||||
|
A lot of nuanced bugs caused by tiny implementation details inside the `revive` compiler _and_ the `pallet-revive` runtime could be identified and eliminated early on thanks to the differential testing strategy. Thus, we decided to take this approach further and created a comprehensive test runner and a large suite of more complex test cases.
|
||||||
|
|
||||||
|
The [Revive Differential Tests](https://github.com/paritytech/revive-differential-tests/) follow the exact same strategy but implement a much more powerful test spec format, spec runner and reports. This allows differentially testing much more complex test cases (for example testing Uniswap pair creations and swaps), executed via transactions sent to actual blockchain nodes.
|
||||||
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# FAQ
|
||||||
|
|
||||||
|
## What EVM version do you support?
|
||||||
|
|
||||||
|
We neither do nor don't support any EVM version. We support Solidity versions, starting from `solc` version 0.8.0 onwards.
|
||||||
|
|
||||||
|
## Is inline assembly supported
|
||||||
|
|
||||||
|
Yes, almost all inline assembly features are supported ([see the differences in Yul translation chapter](user_guide/differences.md)).
|
||||||
|
|
||||||
|
## Do you support opcode `XY`?
|
||||||
|
|
||||||
|
See above, the same applies.
|
||||||
|
|
||||||
|
## In what Solidity version should I write my dApp?
|
||||||
|
|
||||||
|
We generally recommend to always use the latest supported version to profit from latest bugfixes, features and performance improvements.
|
||||||
|
|
||||||
|
Find out about the latest supported version by running `resolc --supported-solc-versions` or checking [here](https://github.com/paritytech/resolc-bin).
|
||||||
|
|
||||||
|
## Tool `XY` says the contract size is larger than 24kb and will fail to deploy?
|
||||||
|
|
||||||
|
The 24kb code size restriction only exist for the EVM. Our limit is currently around 1mb and may increase further in the future.
|
||||||
|
|
||||||
|
## Is `resolc` a drop-in replacement for `solc`?
|
||||||
|
|
||||||
|
No. `resolc` aims to work similarly to `solc`, but it's not considered a drop-in replacement.
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 40 KiB |
@@ -0,0 +1,45 @@
|
|||||||
|
# `revive-runner` sandbox
|
||||||
|
|
||||||
|
Running contract code usually requires a blockchain node. While local dev nodes can be used, sometimes it's just not desirable to do so. Instead, it can be much more convenient to run and debug contract code with a stripped down environment.
|
||||||
|
|
||||||
|
This is where the `revive-runner` comes in handy. In a nutshell, it is a single-binary no-blockchain `pallet-revive` runtime.
|
||||||
|
|
||||||
|
## Installation and usage
|
||||||
|
|
||||||
|
Inside the root `revive` repository directory, install it from source (requires Rust installed):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make install-revive-runner
|
||||||
|
```
|
||||||
|
|
||||||
|
After installing, see `revive-runner --help` for usage help.
|
||||||
|
|
||||||
|
## Trace logs
|
||||||
|
|
||||||
|
The standard `RUST_LOG` environment variable controls the log output from the contract execution. This includes `revive` runtime logs and PVM execution trace logs. Sometimes it's convenient to have more fine granular insight. Some useful filters:
|
||||||
|
- `RUST_LOG=runtime=trace`: The `pallet-revive` runtime trace logs.
|
||||||
|
- `RUST_LOG=polkavm=trace`: Low level PolkaVM instruction tracing.
|
||||||
|
|
||||||
|
## Automatic contract instantiation
|
||||||
|
|
||||||
|
To avoid running the constract in an unitialized state, `revive-runner` automatically instantiates the contract before calling it (constructor arguments can be provided).
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Suppose we want to trace the syscalls of the execution of a compiled contract file `Flipper.pvm`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RUST_LOG=runtime=trace revive-runner -f Flipper.pvm
|
||||||
|
[DEBUG runtime::revive] Contract memory usage: purgable=6144/3145728 KB baseline=103063/1572864
|
||||||
|
[TRACE runtime::revive::strace] call_data_size() = Ok(0) gas_consumed: Weight { ref_time: 985209, proof_size: 0 }
|
||||||
|
[TRACE runtime::revive::strace] value_transferred(out_ptr: 4294836096) = Ok(()) gas_consumed: Weight { ref_time: 2937634, proof_size: 0 }
|
||||||
|
[TRACE runtime::revive::strace] call_data_copy(out_ptr: 131216, out_len: 0, offset: 0) = Ok(()) gas_consumed: Weight { ref_time: 4084483, proof_size: 0 }
|
||||||
|
[TRACE runtime::revive::strace] seal_return(flags: 0, data_ptr: 131216, data_len: 0) = Err(TrapReason::Return(ReturnData { flags: 0, data: [] })) gas_consumed: Weight { ref_time: 5510615, proof_size: 0 }
|
||||||
|
[TRACE runtime::revive] frame finished with: Ok(ExecReturnValue { flags: (empty), data: [] })
|
||||||
|
[TRACE runtime::revive::strace] call_data_size() = Ok(0) gas_consumed: Weight { ref_time: 985209, proof_size: 0 }
|
||||||
|
[TRACE runtime::revive::strace] seal_return(flags: 1, data_ptr: 131088, data_len: 0) = Err(TrapReason::Return(ReturnData { flags: 1, data: [] })) gas_consumed: Weight { ref_time: 2456669, proof_size: 0 }
|
||||||
|
[TRACE runtime::revive] frame finished with: Ok(ExecReturnValue { flags: REVERT, data: [] })
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Roadmap and Vision
|
||||||
|
|
||||||
|
The `revive` compiler speeds up Solidity contracts by orders of magnitude. `revive` provides a decisive edge over other contract platforms. Notably, the compiler eliminates the need of rewriting Solidity dApps as single dApp parachains for scaling reasons. Retaining as high compatibility with Ethereum Solidity as possible keeps entry barriers low.
|
||||||
|
|
||||||
|
We believe in Dr. Gavin Wood's [ĐApps: What Web 3.0 Looks Like](https://gavwood.com/dappsweb3.html) manifesto and the ecosystem of the Solidity programming language. Our motivation lies in the realization that for a _true_ web3 revolution, significant scaling efforts, like the ones provided by the PVM and this project, are necessary to unfold.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
The first major release, `resolc` v1.0.0, emits functional PVM code from given Solidity sources. It relies on `solc` and LLVM for optimizations. The main priority of this release was delivering a mostly feature complete and safe Solidity v0.8.0 compiler.
|
||||||
|
|
||||||
|
Focus for the second major release is on the custom optimization pipeline, which aims to significantly improve emitted code blob sizes.
|
||||||
|
|
||||||
|
The below roadmap gives a rough overview of the project's development timeline.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# `resolc` user guide
|
||||||
|
|
||||||
|
`resolc` is a Solidity `v0.8` compiler for [Polkadot `native` smart contracts](https://docs.polkadot.com/develop/smart-contracts/overview/#native-smart-contracts). Solidity compiled with `resolc` executes orders of magnitude faster than the EVM. `resolc` also supports almost all Solidity `v0.8` features including inline assembly, offering a high level of comptability with the Ethereum Solidity reference implementation.
|
||||||
|
|
||||||
|
## `revive` vs. `resolc` nomenclature
|
||||||
|
|
||||||
|
`revive` is the name of the overarching "Solidity to PolkaVM" compiler project, which contains multiple components (for example the Yul parser, the code generation library, the `resolc` executable itself, and many more things).
|
||||||
|
|
||||||
|
`resolc` is the name of the compiler driver executable, combining many `revive` components in a single and easy to use binary application.
|
||||||
|
|
||||||
|
In other words, `revive` is the whole compiler infrastructure (more like `LLVM`) and `resolc` is a user-facing single-entrypoint compiler frontend (more like `clang`).
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
# CLI usage
|
||||||
|
|
||||||
|
We aim to keep the `resolc` CLI usage close to `solc`. There are a few things and options worthwhile to know about in `resolc` which do not exist in the Ethereum world. This chapter explains those in more detail than the CLI help message.
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
>
|
||||||
|
> For the complete help about CLI options, please see `resolc --help`.
|
||||||
|
|
||||||
|
### LLVM optimization levels
|
||||||
|
```bash
|
||||||
|
-O, --optimization <OPTIMIZATION>
|
||||||
|
```
|
||||||
|
|
||||||
|
`resolc` exposes the optimization level setting for the LLVM backend. The performance and size of compiled contracts varies wiedly between different optimization levels.
|
||||||
|
|
||||||
|
Valid levels are the following:
|
||||||
|
- `0`: No optimizations are applied.
|
||||||
|
- `1`: Basic optimizations for execution time.
|
||||||
|
- `2`: Advanced optimizations for execution time.
|
||||||
|
- `3`: Aggressive optimizations for execution time.
|
||||||
|
- `s`: Optimize for code size.
|
||||||
|
- `z`: Aggressively optimize for code size.
|
||||||
|
|
||||||
|
By default, `-Oz` is applied.
|
||||||
|
|
||||||
|
### Stack size
|
||||||
|
```bash
|
||||||
|
--stack-size <STACK_SIZE>
|
||||||
|
```
|
||||||
|
|
||||||
|
PVM is a register machine with a traditional stack memory space for local variables. This controls the total amount of stack space the contract can use.
|
||||||
|
|
||||||
|
You are incentivized to keep this value as small as possible:
|
||||||
|
1. Increasing the stack size will increase gas costs due to increased startup costs.
|
||||||
|
2. The stack size contributes to the total memory size a contract can use, which includes the contract's code size.
|
||||||
|
|
||||||
|
Default value: 32768
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
>
|
||||||
|
> If the contract uses more stack memory than configured, it will compile fine but eventually revert execution at runtime!
|
||||||
|
|
||||||
|
### Heap size
|
||||||
|
```bash
|
||||||
|
--heap-size <HEAP_SIZE>
|
||||||
|
```
|
||||||
|
|
||||||
|
Unlike the EVM, due to the lack of dynamic memory metering, PVM contracts emulate the EVM heap memory with a static buffer. Consequentially, instead of infinite memory with exponentially growing gas costs, PVM contracts have a finite amount of memory with constant gas costs available.
|
||||||
|
|
||||||
|
You are incentivized to keep this value as small as possible:
|
||||||
|
1.Increasing the heap size will increase startup costs.
|
||||||
|
2.The heap size contributes to the total memory size a contract can use, which includes the contract's code size
|
||||||
|
|
||||||
|
Default value: 65536
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
>
|
||||||
|
> If the contract uses more heap memory than configured, it will compile fine but eventually revert execution at runtime!
|
||||||
|
|
||||||
|
### solc
|
||||||
|
```bash
|
||||||
|
--solc <SOLC>
|
||||||
|
```
|
||||||
|
|
||||||
|
Specify the path to the `solc` executable. By default, the one in `${PATH}` is used.
|
||||||
|
|
||||||
|
### Debug artifacts
|
||||||
|
```bash
|
||||||
|
--debug-output-dir <DEBUG_OUTPUT_DIRECTORY>
|
||||||
|
```
|
||||||
|
|
||||||
|
Dump all intermediary compiler artifacts to files in the specified directory. This includes the YUL IR, optimized and unoptimized LLVM IR, the ELF object and the PVM assembly. Useful for debugging and development purposes.
|
||||||
|
|
||||||
|
### Debug info
|
||||||
|
```bash
|
||||||
|
-g
|
||||||
|
```
|
||||||
|
Generate source based debug information in the output code file. Useful for debugging and development purposes and disabled by default.
|
||||||
|
|
||||||
|
### Deploy time linking
|
||||||
|
```bash
|
||||||
|
--link [--libraries <LIBRARIES>] <INPUT_FILES>
|
||||||
|
```
|
||||||
|
|
||||||
|
In Solidity, 3 things can happen with libraries:
|
||||||
|
|
||||||
|
1. They are not `extern`ally callable and thus can be inlined.
|
||||||
|
1. The solc Solidity optimizer inlines those (usually the case). Note: `resolc` always activates the solc Solidity optimizer.
|
||||||
|
2. If the solc Solidity optimizer is disabled or for some reason fails to inline them (both rare), they are not inlined and require linking.
|
||||||
|
2. They are `extern`ally callable but still linked at compile time. This is the case if at compile time the library address is known (i.e. `--libraries` supplied in CLI or the corresponding setting in STD JSON input).
|
||||||
|
3. They are linked at deploy time. This happens when the compiler does not know the library address (i.e. `--libraries` flag is missing or the provided libraries are incomplete, same for STD JSON input). This case is rare because it's discourage and should never be used by production dApps.
|
||||||
|
|
||||||
|
In cases `1.2` and `3`:
|
||||||
|
- Some of the produced code blobs will be in the "unlinked" raw `ELF` object format and not yet deployable.
|
||||||
|
- To make them deployable, they need to be "linked" (done using the `resolc --link` linker mode explained below).
|
||||||
|
- The compiler emitted `DELEGATECALL` instructions to call non-inlined (unlinked) libraries. The contract deployer must make sure to deploy any libraries prior to contract deployment.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
>
|
||||||
|
> Using deploy time linking is officially **discouraged**. Mainly due to bytecode hashes changing after the fact. We decided to support it in `resolc` regardless, due to popular request.
|
||||||
|
|
||||||
|
Similar to how it works in `solc`, `--libraries` may be used to provide libraries during linking mode.
|
||||||
|
|
||||||
|
Unlike with `solc`, where linking implies a simple string substitution mechanism, `resolc` needs to resolve actual missing `ELF` symbols. This is due to how factory dependencies work in PVM. As a consequence, it isn't sufficient to just provide the unlinked blobs to the linker. Instead, they must be provided in the exact same directory structure the Solidity source code was found during compile time.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
- The contract `src/foo/bar.sol:Bar` is involved in deploy time linking. It may be a factory dependency.
|
||||||
|
- The contract blob needs to be provided inside a relative `src/foo/` directory to `--link`. Otherwise symbol resolution may fail.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> Tooling is supposed to take care of this. In the future, we may append explicit linkage data to simplify the deploy time linking feature.
|
||||||
|
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# Differences to EVM
|
||||||
|
|
||||||
|
This section highlights some potentially observable differences in the [YUL EVM dialect](https://docs.soliditylang.org/en/latest/yul.html#evm-dialect) translation compared to Ethereum Solidity.
|
||||||
|
|
||||||
|
Solidity developers deploying dApps to [`pallet-revive`](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/revive) ought to read and understand this section well.
|
||||||
|
|
||||||
|
## Deploy code vs. runtime code
|
||||||
|
|
||||||
|
Our contract runtime does not differentiate between runtime code and deploy (constructor) code.
|
||||||
|
Instead, both are emitted into a single PVM contract code blob and live on-chain.
|
||||||
|
Therefore, in EVM terminology, the deploy code equals the runtime code.
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
>
|
||||||
|
> In constructor code, the `codesize` instruction will return the call data size instead of the actual code blob size.
|
||||||
|
|
||||||
|
## Solidity
|
||||||
|
|
||||||
|
We are aware of the following differences in the translation of Solidity code.
|
||||||
|
|
||||||
|
### `address.creationCode`
|
||||||
|
|
||||||
|
This returns the bytecode keccak256 hash instead.
|
||||||
|
|
||||||
|
## YUL functions
|
||||||
|
|
||||||
|
The below list contains noteworthy differences in the translation of YUL functions.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> Many functions receive memory buffer offset pointer or size arguments. Since the PVM pointer size is 32 bit, supplying memory offset or buffer size values above `2^32-1` will trap the contract immediately.
|
||||||
|
|
||||||
|
The `solc` compiler ought to always emit valid memory references, so Solidity dApp authors don't need to worry about this unless they deal with low level `assembly` code.
|
||||||
|
|
||||||
|
### `mload`, `mstore`, `msize`, `mcopy` (memory related functions)
|
||||||
|
|
||||||
|
In general, revive preserves the memory layout, meaning low level memory operations are supported. However, a few caveats apply:
|
||||||
|
- The EVM linear heap memory is emulated using a fixed byte buffer of 64kb. This implies that the maximum memory a contract can use is limited to 64kbit (on Ethereum, contract memory is capped by gas and therefore varies).
|
||||||
|
- Thus, accessing memory offsets larger than the fixed buffer size will trap the contract at runtime with an `OutOfBound` error.
|
||||||
|
- The compiler might detect and optimize unused memory reads and writes, leading to a different `msize` compared to what the EVM would see.
|
||||||
|
|
||||||
|
### `calldataload`, `calldatacopy`
|
||||||
|
|
||||||
|
In the constructor code, the offset is ignored and this always returns `0`.
|
||||||
|
|
||||||
|
### `codecopy`
|
||||||
|
|
||||||
|
Only supported in constructor code.
|
||||||
|
|
||||||
|
### `invalid`
|
||||||
|
|
||||||
|
Traps the contract but does not consume the remaining gas.
|
||||||
|
|
||||||
|
### `create`, `create2`
|
||||||
|
|
||||||
|
Deployments on revive work different than on EVM. In a nutshell: Instead of supplying the deploy code concatenated with the constructor arguments (the EVM deploy model), the [revive runtime expects two pointers](https://docs.rs/pallet-revive/latest/pallet_revive/trait.SyscallDoc.html#tymethod.instantiate):
|
||||||
|
1. A buffer containing the code hash to deploy.
|
||||||
|
2. The constructor arguments buffer.
|
||||||
|
|
||||||
|
To make contract instantiation using the `new` keyword in Solidity work seamlessly,
|
||||||
|
`revive` translates the `dataoffset` and `datasize` instructions so that they assume the contract hash instead of the contract code.
|
||||||
|
The hash is always of constant size.
|
||||||
|
Thus, `revive` is able to supply the expected code hash and constructor arguments pointer to the runtime.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
>
|
||||||
|
> This might fall apart in code creating contracts inside `assembly` blocks. **We strongly discourage using the `create` family opcodes to manually craft deployments in `assembly` blocks!** Usually, the reason for using `assembly` blocks is to save gas, which is futile on revive anyways due to lower transaction costs.
|
||||||
|
|
||||||
|
### `dataoffset`
|
||||||
|
|
||||||
|
Returns the contract hash.
|
||||||
|
|
||||||
|
### `datasize`
|
||||||
|
|
||||||
|
Returns the contract hash size (constant value of `32`).
|
||||||
|
|
||||||
|
### `prevrandao`, `difficulty`
|
||||||
|
|
||||||
|
Translates to a constant value of `2500000000000000`.
|
||||||
|
|
||||||
|
### `pc`, `extcodecopy`
|
||||||
|
|
||||||
|
Only valid to use in EVM (they also have no use case in PVM) and produce a compile time error.
|
||||||
|
|
||||||
|
### `blobhash`, `blobbasefee`
|
||||||
|
|
||||||
|
Related to the Ethereum rollup model and produce a compile time error. Polkadot offers a superior rollup model, removing the use case for blob data related opcodes.
|
||||||
|
|
||||||
|
## Difference regarding the `solc` `via-ir` mode
|
||||||
|
|
||||||
|
There are two different compilation pipelines available in `solc` and [there are small differences between them](https://docs.soliditylang.org/en/latest/ir-breaking-changes.html).
|
||||||
|
|
||||||
|
Since `resolc` processes the YUL IR, always assume the `solc` IR based codegen behavior for contracts compiled with the `revive` compiler.
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Installation
|
||||||
|
|
||||||
|
Building Solidity contracts for PolkaVM requires installing the following two compilers:
|
||||||
|
- `solc`: The [Ethereum Solidity reference compiler](https://github.com/argotorg/solidity) implementation.
|
||||||
|
- `resolc`: The revive Solidity compiler YUL frontend and PolkaVM code generator.
|
||||||
|
|
||||||
|
## `resolc` binary releases
|
||||||
|
|
||||||
|
`resolc` is supported an all major operating systems and installation is straightforward.
|
||||||
|
Please find our [binary releases](https://github.com/paritytech/revive/releases) for the following platforms:
|
||||||
|
- Linux (MUSL)
|
||||||
|
- MacOS (universal)
|
||||||
|
- Windows
|
||||||
|
- Wasm via emscripten
|
||||||
|
|
||||||
|
## Installing the `solc` dependency
|
||||||
|
|
||||||
|
`resolc` uses `solc` during the compilation process, please refer to the [Ethereum Solidity documentation](https://docs.soliditylang.org/en/latest/installing-solidity.html) for installation instructions.
|
||||||
|
|
||||||
|
## `revive` NPM package
|
||||||
|
|
||||||
|
We distribute the revive compiler as [node.js module](https://github.com/paritytech/revive/tree/main/js/resolc).
|
||||||
|
|
||||||
|
## Buidling `resolc` from source
|
||||||
|
|
||||||
|
Please follow the build [instructions in the revive `README.md`](https://github.com/paritytech/revive?tab=readme-ov-file#building-from-source).
|
||||||
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# JS NPM package
|
||||||
|
|
||||||
|
The `resolc` compiler driver is published as an NPM package under [@parity/resolc](https://www.npmjs.com/package/@parity/resolc).
|
||||||
|
|
||||||
|
It's usable from `Node.js` code or directly from the command line:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
npx @parity/resolc@latest --bin crates/integration/contracts/flipper.sol -o /tmp/out
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> While the npm package makes a nice portable option, it doesn't expose all options.
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# Rust contract libraries
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> This is not yet implemented but something for consideration on the roadmap.
|
||||||
|
|
||||||
|
Solidity - tightly coupled to the EVM - introduces some inherent inefficiencies that are by design and either needs to be followed or can't be easily worked around, even with efforts like better optimized compiler and VM implementations. This represents a technical dead end. So far the EVM sees no adoption beyond the blockchain industry. Chances are that [the EVM end up deprecated](https://ethereum-magicians.org/t/long-term-l1-execution-layer-proposal-replace-the-evm-with-risc-v) for technical reasons (or maybe not and the RISC-V idea gets abandoned, who knows).
|
||||||
|
|
||||||
|
PVM, however, is a general purpose VM. It supports LLVM based mainstream programming languages like Rust. It's a common software engineering practice to compose applications from pieces written in multiple languages, using each to their own strength. For example, AI solutions traditionally use the python scripting language for convenient developer experience, while the underlying AI models get implemented in a lower level language such as C++.
|
||||||
|
|
||||||
|
The same pattern can of course be applied to dApps, where we'd expect application specific languages like Solidity mixed with libraries implementing computationally complex algorithms in a lower level language. Business logic and user interfaces are naturally implemented as regular Solidity dApps which can include (link against) Rust libraries. Rust is a fast, safe low level language and the Polkadot SDK is written in Rust itself, making it an excellent choice.
|
||||||
|
|
||||||
|
For example, [ZK proof verifiers](https://en.wikipedia.org/wiki/Zero-knowledge_proof) or expensive [DeFi](https://en.wikipedia.org/wiki/Decentralized_finance) primitives would benefit greatly from Rust implementations.
|
||||||
|
|
||||||
|
`revive` provides tooling support and a small Rust contracts SDK for seamless integration with Rust libraries.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Standard JSON interface
|
||||||
|
|
||||||
|
The `revive` compiler is mostly compatible with the `solc` standard JSON interface. There are a few additional (PVM related) __input__ configurations:
|
||||||
|
|
||||||
|
## The `settings.polkavm` object
|
||||||
|
|
||||||
|
Used to configure PVM specific compiler settings.
|
||||||
|
|
||||||
|
### `settings.polkavm.debugInformation`
|
||||||
|
|
||||||
|
A boolean value allowing to enable debug information. Corresponds to `resolc -g`.
|
||||||
|
|
||||||
|
### The `settings.polkavm.memoryConfig` object
|
||||||
|
|
||||||
|
Used to apply PVM specific memory configuration settings.
|
||||||
|
|
||||||
|
#### `settings.polkavm.heapSize`
|
||||||
|
|
||||||
|
A numerical value allowing to configure the contract heap size. Corresponds to `resolc --heap-size`.
|
||||||
|
|
||||||
|
#### `settings.polkavm.stackSize`
|
||||||
|
|
||||||
|
A numerical value allowing to configure the contract stack size. Corresponds to `resolc --stack-size`.
|
||||||
|
|
||||||
|
## The `settings.optimizer` object
|
||||||
|
|
||||||
|
The `settings.optimizer` object is augmented with support for PVM specific optimization settings.
|
||||||
|
|
||||||
|
### `settings.optimizer.mode`
|
||||||
|
|
||||||
|
A single char value to configure the LLVM optimizer settings. Corresponds to `resolc -O`.
|
||||||
|
|
||||||
|
## `settings.llvmArguments`
|
||||||
|
|
||||||
|
Allows to specify arbitrary command line arguments to LLVM initialization. Used mainly for development and debugging purposes.
|
||||||
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Tooling integration
|
||||||
|
|
||||||
|
`resolc` achieved successful integration with a variety of third party developer tools.
|
||||||
|
|
||||||
|
## Solidity toolkits
|
||||||
|
|
||||||
|
Support for `resolc` is available in forks of the [hardhat](https://hardhat.org) and [foundry](https://getfoundry.sh) Solidity toolkits:
|
||||||
|
|
||||||
|
- [The Parity Hardhat fork](https://github.com/paritytech/hardhat-polkadot)
|
||||||
|
- [The Parity Foundry fork](https://github.com/paritytech/foundry-polkadot?tab=readme-ov-file#2-resolc-compiler-integration)
|
||||||
|
|
||||||
|
## Compiler explorer
|
||||||
|
|
||||||
|
`resolc` is available on [godbolt.org](https://godbolt.org/z/6GM6n4Ka3) for the Solidity and Yul input languages. See also the announcement post on the [forum](https://forum.polkadot.network/t/resolc-is-live-on-compiler-explorer).
|
||||||
|
|
||||||
|
## Remix IDE
|
||||||
|
|
||||||
|
There is remix IDE fork with `resolc` support at [remix.polkadot.io](https://remix.polkadot.io). Unfortunately this is no longer actively maintained (there might be bugs and outdated `resolc` versions).
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Welcome
|
||||||
|
|
||||||
|
Hello and a warm welcome to the `revive` Solidity compiler book!
|
||||||
|
|
||||||
|
## Target audience
|
||||||
|
|
||||||
|
- **Solidity dApp developers** should read the [user guide](./user_guide.md). Solidity on PolkaVM introduces important differences to EVM which should be well understood.
|
||||||
|
- **Contributors** will find the [developer guide](./developer_guide.md) helpful for getting up to speed.
|
||||||
|
|
||||||
|
## Other Polkadot contracts resources
|
||||||
|
|
||||||
|
Head to [contracts.polkadot.io](https://docs.polkadot.com/develop/smart-contracts/) for more general information about contracts on Polkadot.
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
This [mdBook](https://github.com/rust-lang/mdBook) documents the revive Solidity compiler project. The content is found under `book/`. Run `make book` to observe changes.
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
INSTALL_DIR="${PWD}/llvm18.0"
|
|
||||||
mkdir -p ${INSTALL_DIR}
|
|
||||||
|
|
||||||
|
|
||||||
# Clone LLVM 18 (any revision after commit bd32aaa is supposed to work)
|
|
||||||
if [ ! -d "llvm-project" ]; then
|
|
||||||
git clone --depth 1 --branch release/18.x https://github.com/llvm/llvm-project.git
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
# Build LLVM, clang
|
|
||||||
LLVM_SRC_PREFIX=${PWD}/llvm-project
|
|
||||||
LLVM_SRC_DIR=${LLVM_SRC_PREFIX}/llvm
|
|
||||||
LLVM_BUILD_DIR=${PWD}/build/llvm
|
|
||||||
if [ ! -d ${LLVM_BUILD_DIR} ] ; then
|
|
||||||
mkdir -p ${LLVM_BUILD_DIR}
|
|
||||||
fi
|
|
||||||
|
|
||||||
cmake -G Ninja \
|
|
||||||
-S ${LLVM_SRC_DIR} \
|
|
||||||
-B ${LLVM_BUILD_DIR} \
|
|
||||||
-DLLVM_ENABLE_ASSERTIONS=On \
|
|
||||||
-DLLVM_ENABLE_TERMINFO=Off \
|
|
||||||
-DLLVM_ENABLE_LIBXML2=Off \
|
|
||||||
-DLLVM_ENABLE_ZLIB=Off \
|
|
||||||
-DLLVM_ENABLE_PROJECTS='clang;lld' \
|
|
||||||
-DLLVM_TARGETS_TO_BUILD='RISCV' \
|
|
||||||
-DLLVM_ENABLE_ZSTD=Off \
|
|
||||||
-DCMAKE_BUILD_TYPE=MinSizeRel \
|
|
||||||
-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}
|
|
||||||
|
|
||||||
cmake --build ${LLVM_BUILD_DIR}
|
|
||||||
cmake --install ${LLVM_BUILD_DIR}
|
|
||||||
|
|
||||||
# Build compiler builtins
|
|
||||||
COMPILER_RT_SRC_DIR=${LLVM_SRC_PREFIX}/compiler-rt
|
|
||||||
COMPILER_RT_BUILD_DIR=${PWD}/build/compiler-rt
|
|
||||||
if [ ! -d ${COMPILER_RT_BUILD_DIR} ] ; then
|
|
||||||
mkdir -p ${COMPILER_RT_BUILD_DIR}
|
|
||||||
fi
|
|
||||||
|
|
||||||
build_compiler_rt() {
|
|
||||||
case "$1" in
|
|
||||||
64) TARGET_ABI=lp64e ;;
|
|
||||||
32) TARGET_ABI=ilp32e ;;
|
|
||||||
*) exit -1
|
|
||||||
esac
|
|
||||||
CFLAGS="--target=riscv${1} -march=rv${1}em -mabi=${TARGET_ABI} -mcpu=generic-rv${1} -nostdlib -nodefaultlibs"
|
|
||||||
|
|
||||||
cmake -G Ninja \
|
|
||||||
-S ${COMPILER_RT_SRC_DIR} \
|
|
||||||
-B ${COMPILER_RT_BUILD_DIR} \
|
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
|
||||||
-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \
|
|
||||||
-DCOMPILER_RT_BUILD_BUILTINS=ON \
|
|
||||||
-DCOMPILER_RT_BUILD_LIBFUZZER=OFF \
|
|
||||||
-DCOMPILER_RT_BUILD_MEMPROF=OFF \
|
|
||||||
-DCOMPILER_RT_BUILD_PROFILE=OFF \
|
|
||||||
-DCOMPILER_RT_BUILD_SANITIZERS=OFF \
|
|
||||||
-DCOMPILER_RT_BUILD_XRAY=OFF \
|
|
||||||
-DCMAKE_C_COMPILER=${INSTALL_DIR}/bin/clang \
|
|
||||||
-DCMAKE_C_COMPILER_TARGET=riscv${1} \
|
|
||||||
-DCMAKE_ASM_COMPILER_TARGET=riscv${1} \
|
|
||||||
-DCMAKE_CXX_COMPILER_TARGET=riscv${1} \
|
|
||||||
-DCMAKE_C_TARGET_BITS=riscv${1} \
|
|
||||||
-DCMAKE_ASM_TARGET_BITS=riscv${1} \
|
|
||||||
-DCMAKE_AR=${INSTALL_DIR}/bin/llvm-ar \
|
|
||||||
-DCMAKE_NM=${INSTALL_DIR}/bin/llvm-nm \
|
|
||||||
-DCMAKE_RANLIB=${INSTALL_DIR}/bin/llvm-ranlib \
|
|
||||||
-DCOMPILER_RT_BAREMETAL_BUILD=ON \
|
|
||||||
-DLLVM_CONFIG_PATH=${INSTALL_DIR}/bin/llvm-config \
|
|
||||||
-DCMAKE_C_FLAGS="${CFLAGS}" \
|
|
||||||
-DCMAKE_ASM_FLAGS="${CFLAGS}" \
|
|
||||||
-DCOMPILER_RT_TEST_COMPILER=${INSTALL_DIR}/bin/clang \
|
|
||||||
-DCMAKE_CXX_FLAGS="${CFLAGS}" \
|
|
||||||
-DCMAKE_SYSTEM_NAME=unknown \
|
|
||||||
-DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON
|
|
||||||
|
|
||||||
cmake --build ${COMPILER_RT_BUILD_DIR}
|
|
||||||
cmake --install ${COMPILER_RT_BUILD_DIR}
|
|
||||||
}
|
|
||||||
|
|
||||||
build_compiler_rt 32
|
|
||||||
build_compiler_rt 64
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "success"
|
|
||||||
echo "add this directory to your PATH: ${INSTALL_DIR}/bin/"
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
large-error-threshold = 192
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
- [Benchmark Results](#benchmark-results)
|
- [Benchmark Results](#benchmark-results)
|
||||||
- [Baseline](#baseline)
|
- [Baseline](#baseline)
|
||||||
- [OddPorduct](#oddporduct)
|
- [OddProduct](#oddproduct)
|
||||||
- [TriangleNumber](#trianglenumber)
|
- [TriangleNumber](#trianglenumber)
|
||||||
- [FibonacciRecursive](#fibonaccirecursive)
|
- [FibonacciRecursive](#fibonaccirecursive)
|
||||||
- [FibonacciIterative](#fibonacciiterative)
|
- [FibonacciIterative](#fibonacciiterative)
|
||||||
@@ -15,59 +15,58 @@
|
|||||||
|
|
||||||
### Baseline
|
### Baseline
|
||||||
|
|
||||||
| | `EVM` | `PVMInterpreter` |
|
| | `EVM` | `PVMInterpreter` |
|
||||||
|:--------|:------------------------|:-------------------------------- |
|
|:--------|:-------------------------|:-------------------------------- |
|
||||||
| **`0`** | `5.97 us` (✅ **1.00x**) | `27.04 us` (❌ *4.53x slower*) |
|
| **`0`** | `10.08 us` (✅ **1.00x**) | `10.32 us` (✅ **1.02x slower**) |
|
||||||
|
|
||||||
### OddPorduct
|
### OddProduct
|
||||||
|
|
||||||
| | `EVM` | `PVMInterpreter` |
|
| | `EVM` | `PVMInterpreter` |
|
||||||
|:-------------|:--------------------------|:-------------------------------- |
|
|:-------------|:--------------------------|:-------------------------------- |
|
||||||
| **`10000`** | `4.26 ms` (✅ **1.00x**) | `2.88 ms` (✅ **1.48x faster**) |
|
| **`10000`** | `3.60 ms` (✅ **1.00x**) | `1.57 ms` (🚀 **2.28x faster**) |
|
||||||
| **`100000`** | `42.37 ms` (✅ **1.00x**) | `28.35 ms` (✅ **1.49x faster**) |
|
| **`100000`** | `34.72 ms` (✅ **1.00x**) | `14.82 ms` (🚀 **2.34x faster**) |
|
||||||
| **`300000`** | `127.88 ms` (✅ **1.00x**) | `88.43 ms` (✅ **1.45x faster**) |
|
| **`300000`** | `105.01 ms` (✅ **1.00x**) | `44.11 ms` (🚀 **2.38x faster**) |
|
||||||
|
|
||||||
### TriangleNumber
|
### TriangleNumber
|
||||||
|
|
||||||
| | `EVM` | `PVMInterpreter` |
|
| | `EVM` | `PVMInterpreter` |
|
||||||
|:-------------|:--------------------------|:-------------------------------- |
|
|:-------------|:-------------------------|:-------------------------------- |
|
||||||
| **`10000`** | `2.85 ms` (✅ **1.00x**) | `2.37 ms` (✅ **1.20x faster**) |
|
| **`10000`** | `2.43 ms` (✅ **1.00x**) | `1.12 ms` (🚀 **2.17x faster**) |
|
||||||
| **`100000`** | `27.85 ms` (✅ **1.00x**) | `23.01 ms` (✅ **1.21x faster**) |
|
| **`100000`** | `24.20 ms` (✅ **1.00x**) | `10.86 ms` (🚀 **2.23x faster**) |
|
||||||
| **`360000`** | `103.01 ms` (✅ **1.00x**) | `83.66 ms` (✅ **1.23x faster**) |
|
| **`360000`** | `88.69 ms` (✅ **1.00x**) | `38.46 ms` (🚀 **2.31x faster**) |
|
||||||
|
|
||||||
### FibonacciRecursive
|
### FibonacciRecursive
|
||||||
|
|
||||||
| | `EVM` | `PVMInterpreter` |
|
| | `EVM` | `PVMInterpreter` |
|
||||||
|:---------|:--------------------------|:--------------------------------- |
|
|:---------|:--------------------------|:--------------------------------- |
|
||||||
| **`12`** | `195.19 us` (✅ **1.00x**) | `333.53 us` (❌ *1.71x slower*) |
|
| **`12`** | `144.17 us` (✅ **1.00x**) | `150.85 us` (✅ **1.05x slower**) |
|
||||||
| **`16`** | `1.22 ms` (✅ **1.00x**) | `1.97 ms` (❌ *1.62x slower*) |
|
| **`16`** | `938.71 us` (✅ **1.00x**) | `922.11 us` (✅ **1.02x faster**) |
|
||||||
| **`20`** | `8.14 ms` (✅ **1.00x**) | `13.20 ms` (❌ *1.62x slower*) |
|
| **`20`** | `6.54 ms` (✅ **1.00x**) | `6.20 ms` (✅ **1.05x faster**) |
|
||||||
| **`24`** | `55.09 ms` (✅ **1.00x**) | `88.56 ms` (❌ *1.61x slower*) |
|
| **`24`** | `45.73 ms` (✅ **1.00x**) | `41.98 ms` (✅ **1.09x faster**) |
|
||||||
|
|
||||||
### FibonacciIterative
|
### FibonacciIterative
|
||||||
|
|
||||||
| | `EVM` | `PVMInterpreter` |
|
| | `EVM` | `PVMInterpreter` |
|
||||||
|:----------|:-------------------------|:--------------------------------- |
|
|:----------|:-------------------------|:-------------------------------- |
|
||||||
| **`64`** | `33.39 us` (✅ **1.00x**) | `86.02 us` (❌ *2.58x slower*) |
|
| **`64`** | `23.00 us` (✅ **1.00x**) | `31.88 us` (❌ *1.39x slower*) |
|
||||||
| **`128`** | `52.91 us` (✅ **1.00x**) | `126.38 us` (❌ *2.39x slower*) |
|
| **`128`** | `35.28 us` (✅ **1.00x**) | `42.43 us` (❌ *1.20x slower*) |
|
||||||
| **`256`** | `82.33 us` (✅ **1.00x**) | `208.74 us` (❌ *2.54x slower*) |
|
| **`256`** | `60.12 us` (✅ **1.00x**) | `61.20 us` (✅ **1.02x slower**) |
|
||||||
|
|
||||||
### FibonacciBinet
|
### FibonacciBinet
|
||||||
|
|
||||||
| | `EVM` | `PVMInterpreter` |
|
| | `EVM` | `PVMInterpreter` |
|
||||||
|:----------|:-------------------------|:--------------------------------- |
|
|:----------|:-------------------------|:-------------------------------- |
|
||||||
| **`64`** | `32.29 us` (✅ **1.00x**) | `161.75 us` (❌ *5.01x slower*) |
|
| **`64`** | `23.01 us` (✅ **1.00x**) | `47.74 us` (❌ *2.07x slower*) |
|
||||||
| **`128`** | `36.02 us` (✅ **1.00x**) | `172.59 us` (❌ *4.79x slower*) |
|
| **`128`** | `25.44 us` (✅ **1.00x**) | `49.67 us` (❌ *1.95x slower*) |
|
||||||
| **`256`** | `41.21 us` (✅ **1.00x**) | `185.30 us` (❌ *4.50x slower*) |
|
| **`256`** | `28.66 us` (✅ **1.00x**) | `53.01 us` (❌ *1.85x slower*) |
|
||||||
|
|
||||||
### SHA1
|
### SHA1
|
||||||
|
|
||||||
| | `EVM` | `PVMInterpreter` |
|
| | `EVM` | `PVMInterpreter` |
|
||||||
|:----------|:--------------------------|:--------------------------------- |
|
|:----------|:--------------------------|:--------------------------------- |
|
||||||
| **`1`** | `160.17 us` (✅ **1.00x**) | `403.46 us` (❌ *2.52x slower*) |
|
| **`1`** | `135.87 us` (✅ **1.00x**) | `243.75 us` (❌ *1.79x slower*) |
|
||||||
| **`64`** | `286.69 us` (✅ **1.00x**) | `479.79 us` (❌ *1.67x slower*) |
|
| **`64`** | `258.45 us` (✅ **1.00x**) | `355.70 us` (❌ *1.38x slower*) |
|
||||||
| **`512`** | `1.18 ms` (✅ **1.00x**) | `1.37 ms` (❌ *1.16x slower*) |
|
| **`512`** | `1.10 ms` (✅ **1.00x**) | `1.09 ms` (✅ **1.01x faster**) |
|
||||||
|
|
||||||
---
|
---
|
||||||
Made with [criterion-table](https://github.com/nu11ptr/criterion-table)
|
Made with [criterion-table](https://github.com/nu11ptr/criterion-table)
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ fn group<'error, M>(c: &'error mut Criterion<M>, group_name: &str) -> BenchmarkG
|
|||||||
where
|
where
|
||||||
M: Measurement,
|
M: Measurement,
|
||||||
{
|
{
|
||||||
return c.benchmark_group(group_name);
|
c.benchmark_group(group_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bench_baseline(c: &mut Criterion) {
|
fn bench_baseline(c: &mut Criterion) {
|
||||||
@@ -57,7 +57,7 @@ fn bench_baseline(c: &mut Criterion) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn bench_odd_product(c: &mut Criterion) {
|
fn bench_odd_product(c: &mut Criterion) {
|
||||||
let group = group(c, "OddPorduct");
|
let group = group(c, "OddProduct");
|
||||||
let parameters = &[10_000, 100_000, 300000];
|
let parameters = &[10_000, 100_000, 300000];
|
||||||
|
|
||||||
bench(group, parameters, parameters, Contract::odd_product);
|
bench(group, parameters, parameters, Contract::odd_product);
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
/target/
|
||||||
|
|
||||||
|
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||||
|
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||||
|
Cargo.lock
|
||||||
|
|
||||||
|
# These are backup files generated by rustfmt
|
||||||
|
**/*.rs.bk
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
/.idea/
|
||||||
|
/.vscode/
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "revive-build-utils"
|
||||||
|
version = "0.2.0"
|
||||||
|
license.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
authors = [
|
||||||
|
"Cyrill Leutwiler <cyrill@parity.io>",
|
||||||
|
]
|
||||||
|
description = "Shared build utilities of the revive compiler"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
doctest = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# revive: Compiler Common
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This library is distributed under the terms of either
|
||||||
|
|
||||||
|
- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
|
||||||
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||||
|
|
||||||
|
at your option.
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
//! The compiler build utilities library.
|
||||||
|
|
||||||
|
/// The revive LLVM host dependency directory prefix environment variable.
|
||||||
|
pub const REVIVE_LLVM_HOST_PREFIX: &str = "LLVM_SYS_181_PREFIX";
|
||||||
|
|
||||||
|
/// The revive LLVM target dependency directory prefix environment variable.
|
||||||
|
pub const REVIVE_LLVM_TARGET_PREFIX: &str = "REVIVE_LLVM_TARGET_PREFIX";
|
||||||
|
|
||||||
|
/// The revive LLVM host tool help link.
|
||||||
|
pub const REVIVE_LLVM_BUILDER_HELP_LINK: &str =
|
||||||
|
"https://github.com/paritytech/revive?tab=readme-ov-file#building-from-source";
|
||||||
|
|
||||||
|
/// Constructs a path to the LLVM tool `name`.
|
||||||
|
///
|
||||||
|
/// Respects the [`REVIVE_LLVM_HOST_PREFIX`] environment variable.
|
||||||
|
pub fn llvm_host_tool(name: &str) -> std::path::PathBuf {
|
||||||
|
std::env::var_os(REVIVE_LLVM_HOST_PREFIX)
|
||||||
|
.map(Into::<std::path::PathBuf>::into)
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
panic!("install LLVM using the revive-llvm builder and export '{REVIVE_LLVM_HOST_PREFIX}'; see also: {REVIVE_LLVM_BUILDER_HELP_LINK}")
|
||||||
|
})
|
||||||
|
.join("bin")
|
||||||
|
.join(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the LLVM lib dir.
|
||||||
|
///
|
||||||
|
/// Respects the [`REVIVE_LLVM_HOST_PREFIX`] environment variable.
|
||||||
|
pub fn llvm_lib_dir() -> std::path::PathBuf {
|
||||||
|
std::path::PathBuf::from(llvm_config("--libdir").trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the LLVM CXX compiler flags.
|
||||||
|
///
|
||||||
|
/// Respects the [`REVIVE_LLVM_HOST_PREFIX`] environment variable.
|
||||||
|
pub fn llvm_cxx_flags() -> String {
|
||||||
|
llvm_config("--cxxflags")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute the `llvm-config` utility respecting the [`REVIVE_LLVM_HOST_PREFIX`] environment variable.
|
||||||
|
fn llvm_config(arg: &str) -> String {
|
||||||
|
let llvm_config = llvm_host_tool("llvm-config");
|
||||||
|
let output = std::process::Command::new(&llvm_config)
|
||||||
|
.arg(arg)
|
||||||
|
.output()
|
||||||
|
.unwrap_or_else(|error| panic!("`{} {arg}` failed: {error}", llvm_config.display()));
|
||||||
|
|
||||||
|
String::from_utf8(output.stdout)
|
||||||
|
.unwrap_or_else(|_| panic!("output of `{} {arg}` should be utf8", llvm_config.display()))
|
||||||
|
}
|
||||||
@@ -8,7 +8,5 @@ authors.workspace = true
|
|||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
description = "compiler builtins for the revive compiler"
|
description = "compiler builtins for the revive compiler"
|
||||||
|
|
||||||
[features]
|
[build-dependencies]
|
||||||
riscv-64 = []
|
revive-build-utils = { workspace = true }
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
|
|||||||
+18
-14
@@ -1,30 +1,34 @@
|
|||||||
use std::{env, fs, io::Read, path::Path, process::Command};
|
use std::{env, fs, io::Read, path::Path, process::Command};
|
||||||
|
|
||||||
#[cfg(not(feature = "riscv-64"))]
|
|
||||||
pub const BUILTINS_ARCHIVE_FILE: &str = "libclang_rt.builtins-riscv32.a";
|
|
||||||
#[cfg(feature = "riscv-64")]
|
|
||||||
pub const BUILTINS_ARCHIVE_FILE: &str = "libclang_rt.builtins-riscv64.a";
|
pub const BUILTINS_ARCHIVE_FILE: &str = "libclang_rt.builtins-riscv64.a";
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut llvm_lib_dir = String::new();
|
println!(
|
||||||
|
"cargo:rerun-if-env-changed={}",
|
||||||
|
revive_build_utils::REVIVE_LLVM_HOST_PREFIX
|
||||||
|
);
|
||||||
|
|
||||||
Command::new("llvm-config")
|
let llvm_config = revive_build_utils::llvm_host_tool("llvm-config");
|
||||||
.args(["--libdir"])
|
let mut llvm_lib_dir = String::new();
|
||||||
|
Command::new(&llvm_config)
|
||||||
|
.arg("--libdir")
|
||||||
.output()
|
.output()
|
||||||
.expect("llvm-config should be able to provide LD path")
|
.unwrap_or_else(|_| {
|
||||||
|
panic!(
|
||||||
|
"{} should be able to provide LD path",
|
||||||
|
llvm_config.display()
|
||||||
|
)
|
||||||
|
})
|
||||||
.stdout
|
.stdout
|
||||||
.as_slice()
|
.as_slice()
|
||||||
.read_to_string(&mut llvm_lib_dir)
|
.read_to_string(&mut llvm_lib_dir)
|
||||||
.expect("llvm-config output should be utf8");
|
.expect("llvm-config output should be utf8");
|
||||||
|
|
||||||
let mut lib_path = std::path::PathBuf::from(llvm_lib_dir.trim())
|
let lib_path = revive_build_utils::llvm_lib_dir()
|
||||||
.join("linux")
|
.join("unknown")
|
||||||
.join(BUILTINS_ARCHIVE_FILE);
|
.join(BUILTINS_ARCHIVE_FILE);
|
||||||
if !lib_path.exists() {
|
let archive = fs::read(&lib_path).expect("clang builtins not found");
|
||||||
lib_path = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
|
println!("cargo:rerun-if-env-changed={}", lib_path.display());
|
||||||
.join(BUILTINS_ARCHIVE_FILE);
|
|
||||||
}
|
|
||||||
let archive = fs::read(lib_path).expect("clang builtins not found");
|
|
||||||
|
|
||||||
let out_dir = env::var_os("OUT_DIR").expect("has OUT_DIR");
|
let out_dir = env::var_os("OUT_DIR").expect("has OUT_DIR");
|
||||||
let archive_path = Path::new(&out_dir).join(BUILTINS_ARCHIVE_FILE);
|
let archive_path = Path::new(&out_dir).join(BUILTINS_ARCHIVE_FILE);
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revive-common"
|
name = "revive-common"
|
||||||
version.workspace = true
|
version = "0.2.1"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
@@ -13,11 +13,10 @@ description = "Shared constants of the revive compiler"
|
|||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[features]
|
|
||||||
riscv-64 = []
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
hex = { workspace = true }
|
||||||
|
sha3 = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true, features = [ "arbitrary_precision", "unbounded_depth" ] }
|
serde_json = { workspace = true, features = [ "arbitrary_precision", "unbounded_depth" ] }
|
||||||
serde_stacker = { workspace = true }
|
serde_stacker = { workspace = true }
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ pub const BIT_LENGTH_ETH_ADDRESS: usize =
|
|||||||
pub const BIT_LENGTH_WORD: usize = crate::byte_length::BYTE_LENGTH_WORD * BIT_LENGTH_BYTE;
|
pub const BIT_LENGTH_WORD: usize = crate::byte_length::BYTE_LENGTH_WORD * BIT_LENGTH_BYTE;
|
||||||
|
|
||||||
/// Bit length of the runtime value type.
|
/// Bit length of the runtime value type.
|
||||||
pub const BIT_LENGTH_VALUE: usize = crate::byte_length::BYTE_LENGTH_VALUE * BIT_LENGTH_BYTE;
|
pub const BIT_LENGTH_VALUE: usize = BIT_LENGTH_WORD;
|
||||||
|
|
||||||
/// Bit length of thre runimte block number type.
|
/// Bit length of thre runimte block number type.
|
||||||
pub const BIT_LENGTH_BLOCK_NUMBER: usize =
|
pub const BIT_LENGTH_BLOCK_NUMBER: usize =
|
||||||
|
|||||||
@@ -6,15 +6,12 @@ pub const BYTE_LENGTH_BYTE: usize = 1;
|
|||||||
/// The x86 word byte-length.
|
/// The x86 word byte-length.
|
||||||
pub const BYTE_LENGTH_X32: usize = 4;
|
pub const BYTE_LENGTH_X32: usize = 4;
|
||||||
|
|
||||||
/// Native stack alignment size in bytes
|
|
||||||
#[cfg(not(feature = "riscv-64"))]
|
|
||||||
pub const BYTE_LENGTH_STACK_ALIGN: usize = 4;
|
|
||||||
#[cfg(feature = "riscv-64")]
|
|
||||||
pub const BYTE_LENGTH_STACK_ALIGN: usize = 8;
|
|
||||||
|
|
||||||
/// The x86_64 word byte-length.
|
/// The x86_64 word byte-length.
|
||||||
pub const BYTE_LENGTH_X64: usize = 8;
|
pub const BYTE_LENGTH_X64: usize = 8;
|
||||||
|
|
||||||
|
/// EVM native stack alignment size in bytes
|
||||||
|
pub const BYTE_LENGTH_STACK_ALIGN: usize = 32;
|
||||||
|
|
||||||
/// The ETH address byte-length.
|
/// The ETH address byte-length.
|
||||||
pub const BYTE_LENGTH_ETH_ADDRESS: usize = 20;
|
pub const BYTE_LENGTH_ETH_ADDRESS: usize = 20;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
//! The contract identifier helper library.
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// This structure simplifies passing the contract identifiers through the compilation pipeline.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ContractIdentifier {
|
||||||
|
/// The absolute file path.
|
||||||
|
pub path: String,
|
||||||
|
/// The contract name.
|
||||||
|
/// Is set for Solidity contracts only. Otherwise it would be equal to the file name.
|
||||||
|
pub name: Option<String>,
|
||||||
|
/// The full contract identifier.
|
||||||
|
/// For Solidity, The format is `<absolute file path>:<contract name>`.
|
||||||
|
/// For other languages, `<absolute file path>`.
|
||||||
|
pub full_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContractIdentifier {
|
||||||
|
/// A shortcut constructor.
|
||||||
|
pub fn new(path: String, name: Option<String>) -> Self {
|
||||||
|
let full_path = match name {
|
||||||
|
Some(ref name) => format!("{path}:{name}"),
|
||||||
|
None => path.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
path,
|
||||||
|
name,
|
||||||
|
full_path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,6 +43,12 @@ pub enum EVMVersion {
|
|||||||
/// The corresponding EVM version.
|
/// The corresponding EVM version.
|
||||||
#[serde(rename = "cancun")]
|
#[serde(rename = "cancun")]
|
||||||
Cancun,
|
Cancun,
|
||||||
|
/// The corresponding EVM version.
|
||||||
|
#[serde(rename = "prague")]
|
||||||
|
Prague,
|
||||||
|
/// The corresponding EVM version.
|
||||||
|
#[serde(rename = "osaka")]
|
||||||
|
Osaka,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for EVMVersion {
|
impl TryFrom<&str> for EVMVersion {
|
||||||
@@ -62,6 +68,8 @@ impl TryFrom<&str> for EVMVersion {
|
|||||||
"paris" => Self::Paris,
|
"paris" => Self::Paris,
|
||||||
"shanghai" => Self::Shanghai,
|
"shanghai" => Self::Shanghai,
|
||||||
"cancun" => Self::Cancun,
|
"cancun" => Self::Cancun,
|
||||||
|
"prague" => Self::Prague,
|
||||||
|
"osaka" => Self::Osaka,
|
||||||
_ => anyhow::bail!("Invalid EVM version: {}", value),
|
_ => anyhow::bail!("Invalid EVM version: {}", value),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -82,6 +90,8 @@ impl std::fmt::Display for EVMVersion {
|
|||||||
Self::Paris => write!(f, "paris"),
|
Self::Paris => write!(f, "paris"),
|
||||||
Self::Shanghai => write!(f, "shanghai"),
|
Self::Shanghai => write!(f, "shanghai"),
|
||||||
Self::Cancun => write!(f, "cancun"),
|
Self::Cancun => write!(f, "cancun"),
|
||||||
|
Self::Prague => write!(f, "prague"),
|
||||||
|
Self::Osaka => write!(f, "osaka"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,6 @@ pub static EXTENSION_ABI: &str = "abi";
|
|||||||
/// The Yul IR file extension.
|
/// The Yul IR file extension.
|
||||||
pub static EXTENSION_YUL: &str = "yul";
|
pub static EXTENSION_YUL: &str = "yul";
|
||||||
|
|
||||||
/// The EVM legacy assembly IR file extension.
|
|
||||||
pub static EXTENSION_EVMLA: &str = "evmla";
|
|
||||||
|
|
||||||
/// The Ethereal IR file extension.
|
|
||||||
pub static EXTENSION_ETHIR: &str = "ethir";
|
|
||||||
|
|
||||||
/// The EVM file extension.
|
/// The EVM file extension.
|
||||||
pub static EXTENSION_EVM: &str = "evm";
|
pub static EXTENSION_EVM: &str = "evm";
|
||||||
|
|
||||||
@@ -41,3 +35,6 @@ pub static EXTENSION_POLKAVM_ASSEMBLY: &str = "pvmasm";
|
|||||||
|
|
||||||
/// The PolkaVM bytecode file extension.
|
/// The PolkaVM bytecode file extension.
|
||||||
pub static EXTENSION_POLKAVM_BINARY: &str = "pvm";
|
pub static EXTENSION_POLKAVM_BINARY: &str = "pvm";
|
||||||
|
|
||||||
|
/// The ELF shared object file extension.
|
||||||
|
pub static EXTENSION_OBJECT: &str = "o";
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
//! Keccak-256 hash utilities.
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sha3::digest::FixedOutput;
|
||||||
|
use sha3::Digest;
|
||||||
|
|
||||||
|
pub const DIGEST_BYTES: usize = 32;
|
||||||
|
|
||||||
|
/// Keccak-256 hash utilities.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Keccak256 {
|
||||||
|
/// Binary representation.
|
||||||
|
bytes: [u8; DIGEST_BYTES],
|
||||||
|
/// Hexadecimal string representation.
|
||||||
|
string: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Keccak256 {
|
||||||
|
/// Computes the `keccak256` hash for `preimage`.
|
||||||
|
pub fn from_slice(preimage: &[u8]) -> Self {
|
||||||
|
let bytes = sha3::Keccak256::digest(preimage).into();
|
||||||
|
let string = format!("0x{}", hex::encode(bytes));
|
||||||
|
Self { bytes, string }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the `keccak256` hash for an array of `preimages`.
|
||||||
|
pub fn from_slices<R: AsRef<[u8]>>(preimages: &[R]) -> Self {
|
||||||
|
let mut hasher = sha3::Keccak256::new();
|
||||||
|
for preimage in preimages.iter() {
|
||||||
|
hasher.update(preimage);
|
||||||
|
}
|
||||||
|
let bytes: [u8; DIGEST_BYTES] = hasher.finalize_fixed().into();
|
||||||
|
let string = format!("0x{}", hex::encode(bytes));
|
||||||
|
Self { bytes, string }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the 32-byte SHA-3 hash.
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
self.bytes.as_slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the hexadecimal string representation.
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
self.string.as_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts the binary representation.
|
||||||
|
pub fn to_vec(&self) -> Vec<u8> {
|
||||||
|
self.bytes.to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Keccak256 {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn hash_and_stringify_works() {
|
||||||
|
assert_eq!(
|
||||||
|
super::Keccak256::from_slices(&["foo".as_bytes(), "bar".as_bytes(),]).as_str(),
|
||||||
|
"0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,13 @@
|
|||||||
pub(crate) mod base;
|
pub(crate) mod base;
|
||||||
pub(crate) mod bit_length;
|
pub(crate) mod bit_length;
|
||||||
pub(crate) mod byte_length;
|
pub(crate) mod byte_length;
|
||||||
|
pub(crate) mod contract_identifier;
|
||||||
pub(crate) mod evm_version;
|
pub(crate) mod evm_version;
|
||||||
pub(crate) mod exit_code;
|
pub(crate) mod exit_code;
|
||||||
pub(crate) mod extension;
|
pub(crate) mod extension;
|
||||||
|
pub(crate) mod keccak256;
|
||||||
|
pub(crate) mod metadata;
|
||||||
|
pub(crate) mod object;
|
||||||
pub(crate) mod utils;
|
pub(crate) mod utils;
|
||||||
|
|
||||||
pub use self::base::*;
|
pub use self::base::*;
|
||||||
@@ -14,4 +18,8 @@ pub use self::byte_length::*;
|
|||||||
pub use self::evm_version::EVMVersion;
|
pub use self::evm_version::EVMVersion;
|
||||||
pub use self::exit_code::*;
|
pub use self::exit_code::*;
|
||||||
pub use self::extension::*;
|
pub use self::extension::*;
|
||||||
|
pub use self::keccak256::*;
|
||||||
|
pub use self::metadata::*;
|
||||||
|
pub use self::object::*;
|
||||||
pub use self::utils::*;
|
pub use self::utils::*;
|
||||||
|
pub use contract_identifier::*;
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
//! The metadata hash type.
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// The metadata hash type.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
pub enum MetadataHash {
|
||||||
|
/// Do not include bytecode hash.
|
||||||
|
#[serde(rename = "none")]
|
||||||
|
None,
|
||||||
|
/// Include the `ipfs` hash.
|
||||||
|
#[serde(rename = "ipfs")]
|
||||||
|
IPFS,
|
||||||
|
/// Include the `keccak256`` hash.
|
||||||
|
#[serde(rename = "keccak256")]
|
||||||
|
Keccak256,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for MetadataHash {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(string: &str) -> Result<Self, Self::Err> {
|
||||||
|
match string {
|
||||||
|
"none" => Ok(Self::None),
|
||||||
|
"ipfs" => Ok(Self::IPFS),
|
||||||
|
"keccak256" => Ok(Self::Keccak256),
|
||||||
|
string => anyhow::bail!("unknown bytecode hash mode: `{string}`"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for MetadataHash {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::None => write!(f, "none"),
|
||||||
|
Self::IPFS => write!(f, "ipfs"),
|
||||||
|
Self::Keccak256 => write!(f, "keccak256"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
//! The revive binary object helper module.
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// The binary object format.
|
||||||
|
///
|
||||||
|
/// Unlinked contracts are stored in a different object format
|
||||||
|
/// than final (linked) contract blobs.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
pub enum ObjectFormat {
|
||||||
|
/// The unlinked ELF object format.
|
||||||
|
ELF,
|
||||||
|
/// The fully linked PVM format.
|
||||||
|
PVM,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectFormat {
|
||||||
|
pub const PVM_MAGIC: [u8; 4] = [b'P', b'V', b'M', b'\0'];
|
||||||
|
pub const ELF_MAGIC: [u8; 4] = [0x7f, b'E', b'L', b'F'];
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ObjectFormat {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||||
|
match value {
|
||||||
|
"ELF" => Ok(Self::ELF),
|
||||||
|
"PVM" => Ok(Self::PVM),
|
||||||
|
_ => anyhow::bail!(
|
||||||
|
"Unknown object format: {value}. Supported formats: {}, {}",
|
||||||
|
Self::ELF,
|
||||||
|
Self::PVM,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[u8]> for ObjectFormat {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
||||||
|
if value.starts_with(&Self::PVM_MAGIC) {
|
||||||
|
return Ok(Self::PVM);
|
||||||
|
}
|
||||||
|
if value.starts_with(&Self::ELF_MAGIC) {
|
||||||
|
return Ok(Self::ELF);
|
||||||
|
}
|
||||||
|
Err("expected a contract object")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ObjectFormat {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::ELF => write!(f, "ELF"),
|
||||||
|
Self::PVM => write!(f, "PVM"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +1,45 @@
|
|||||||
//! The compiler common utils.
|
//! The compiler common utils.
|
||||||
|
|
||||||
/// Deserializes a `serde_json` object from slice with the recursion limit disabled.
|
/// Deserializes a `serde_json` object from slice with the recursion limit disabled.
|
||||||
|
///
|
||||||
/// Must be used for all JSON I/O to avoid crashes due to the aforementioned limit.
|
/// Must be used for all JSON I/O to avoid crashes due to the aforementioned limit.
|
||||||
pub fn deserialize_from_slice<O>(input: &[u8]) -> anyhow::Result<O>
|
pub fn deserialize_from_slice<O>(input: &[u8]) -> anyhow::Result<O>
|
||||||
where
|
where
|
||||||
O: serde::de::DeserializeOwned,
|
O: serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
let mut deserializer = serde_json::Deserializer::from_slice(input);
|
let deserializer = serde_json::Deserializer::from_slice(input);
|
||||||
deserializer.disable_recursion_limit();
|
deserialize(deserializer)
|
||||||
let deserializer = serde_stacker::Deserializer::new(&mut deserializer);
|
|
||||||
let result = O::deserialize(deserializer)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserializes a `serde_json` object from string with the recursion limit disabled.
|
/// Deserializes a `serde_json` object from string with the recursion limit disabled.
|
||||||
|
///
|
||||||
/// Must be used for all JSON I/O to avoid crashes due to the aforementioned limit.
|
/// Must be used for all JSON I/O to avoid crashes due to the aforementioned limit.
|
||||||
pub fn deserialize_from_str<O>(input: &str) -> anyhow::Result<O>
|
pub fn deserialize_from_str<O>(input: &str) -> anyhow::Result<O>
|
||||||
where
|
where
|
||||||
O: serde::de::DeserializeOwned,
|
O: serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
let mut deserializer = serde_json::Deserializer::from_str(input);
|
let deserializer = serde_json::Deserializer::from_str(input);
|
||||||
|
deserialize(deserializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserializes a `serde_json` object from reader with the recursion limit disabled.
|
||||||
|
///
|
||||||
|
/// Must be used for all JSON I/O to avoid crashes due to the aforementioned limit.
|
||||||
|
pub fn deserialize_from_reader<R, O>(reader: R) -> anyhow::Result<O>
|
||||||
|
where
|
||||||
|
R: std::io::Read,
|
||||||
|
O: serde::de::DeserializeOwned,
|
||||||
|
{
|
||||||
|
let deserializer = serde_json::Deserializer::from_reader(reader);
|
||||||
|
deserialize(deserializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs the generic deserializer.
|
||||||
|
pub fn deserialize<'de, R, O>(mut deserializer: serde_json::Deserializer<R>) -> anyhow::Result<O>
|
||||||
|
where
|
||||||
|
R: serde_json::de::Read<'de>,
|
||||||
|
O: serde::de::DeserializeOwned,
|
||||||
|
{
|
||||||
deserializer.disable_recursion_limit();
|
deserializer.disable_recursion_limit();
|
||||||
let deserializer = serde_stacker::Deserializer::new(&mut deserializer);
|
let deserializer = serde_stacker::Deserializer::new(&mut deserializer);
|
||||||
let result = O::deserialize(deserializer)?;
|
let result = O::deserialize(deserializer)?;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revive-differential"
|
name = "revive-differential"
|
||||||
version.workspace = true
|
description = "utilities for differential testing the revive compiler against EVM"
|
||||||
|
version = "0.2.0"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"chainId": 1,
|
"chainId": 420420420,
|
||||||
"homesteadBlock": 0,
|
"homesteadBlock": 0,
|
||||||
"eip150Block": 0,
|
"eip150Block": 0,
|
||||||
"eip155Block": 0,
|
"eip155Block": 0,
|
||||||
@@ -15,10 +15,29 @@
|
|||||||
"grayGlacierBlock": 0,
|
"grayGlacierBlock": 0,
|
||||||
"shanghaiTime": 0,
|
"shanghaiTime": 0,
|
||||||
"cancunTime": 0,
|
"cancunTime": 0,
|
||||||
|
"pragueTime": 0,
|
||||||
|
"osakaTime": 0,
|
||||||
"terminalTotalDifficulty": 0,
|
"terminalTotalDifficulty": 0,
|
||||||
"terminalTotalDifficultyPassed": true
|
"terminalTotalDifficultyPassed": true,
|
||||||
|
"blobSchedule": {
|
||||||
|
"cancun": {
|
||||||
|
"target": 3,
|
||||||
|
"max": 6,
|
||||||
|
"baseFeeUpdateFraction": 3338477
|
||||||
|
},
|
||||||
|
"prague": {
|
||||||
|
"target": 6,
|
||||||
|
"max": 9,
|
||||||
|
"baseFeeUpdateFraction": 5007716
|
||||||
|
},
|
||||||
|
"osaka": {
|
||||||
|
"target": 6,
|
||||||
|
"max": 9,
|
||||||
|
"baseFeeUpdateFraction": 5007716
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
"coinbase": "0xffffffffffffffffffffffffffffffffffffffff",
|
||||||
"difficulty": "0x20000",
|
"difficulty": "0x20000",
|
||||||
"extraData": "",
|
"extraData": "",
|
||||||
"gasLimit": "0xffffffff",
|
"gasLimit": "0xffffffff",
|
||||||
@@ -26,5 +45,15 @@
|
|||||||
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"timestamp": "0x00",
|
"timestamp": "0x00",
|
||||||
"alloc": {}
|
"alloc": {
|
||||||
|
"0101010101010101010101010101010101010101": {
|
||||||
|
"balance": "1000000000"
|
||||||
|
},
|
||||||
|
"0202020202020202020202020202020202020202": {
|
||||||
|
"balance": "1000000000"
|
||||||
|
},
|
||||||
|
"0303030303030303030303030303030303030303": {
|
||||||
|
"balance": "1000000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,8 +3,8 @@ use std::time::Duration;
|
|||||||
/// Parse a go formatted duration.
|
/// Parse a go formatted duration.
|
||||||
///
|
///
|
||||||
/// Sources:
|
/// Sources:
|
||||||
/// - https://crates.io/crates/go-parse-duration (fixed an utf8 bug)
|
/// - <https://crates.io/crates/go-parse-duration> (fixed an utf8 bug)
|
||||||
/// - https://github.com/golang/go/blob/master/src/time/format.go
|
/// - <https://github.com/golang/go/blob/master/src/time/format.go>
|
||||||
pub fn parse_go_duration(value: &str) -> Result<Duration, String> {
|
pub fn parse_go_duration(value: &str) -> Result<Duration, String> {
|
||||||
parse_duration(value).map(|ns| Duration::from_nanos(ns.unsigned_abs()))
|
parse_duration(value).map(|ns| Duration::from_nanos(ns.unsigned_abs()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const EXECUTABLE_ARGS_BENCH: [&str; 6] = [
|
|||||||
"-",
|
"-",
|
||||||
];
|
];
|
||||||
const GAS_USED_MARKER: &str = "EVM gas used:";
|
const GAS_USED_MARKER: &str = "EVM gas used:";
|
||||||
const REVERT_MARKER: &str = "error: execution reverted";
|
const REVERT_MARKER: &str = " error: ";
|
||||||
|
|
||||||
/// The geth EVM state dump structure
|
/// The geth EVM state dump structure
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
@@ -100,10 +100,9 @@ pub struct EvmOutput {
|
|||||||
impl EvmOutput {
|
impl EvmOutput {
|
||||||
/// Return if there was no error found.
|
/// Return if there was no error found.
|
||||||
///
|
///
|
||||||
/// Panics if the gas used is zero as this indicates nothing was run, i.e.
|
/// Panics if the gas used is zero as this indicates nothing was run.
|
||||||
/// there was no receiving account but still no error is reported.
|
|
||||||
pub fn run_success(&self) -> bool {
|
pub fn run_success(&self) -> bool {
|
||||||
assert_ne!(self.gas_used, U256::ZERO, "nothing was executed");
|
assert_ne!(self.gas_used, U256::ZERO, "nothing was executed: {self:?}");
|
||||||
self.error.is_none()
|
self.error.is_none()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -414,7 +413,7 @@ impl Evm {
|
|||||||
let stderr = str::from_utf8(output.stderr.as_slice())
|
let stderr = str::from_utf8(output.stderr.as_slice())
|
||||||
.unwrap_or_else(|err| panic!("{EXECUTABLE_NAME} stderr failed to parse: {err}"));
|
.unwrap_or_else(|err| panic!("{EXECUTABLE_NAME} stderr failed to parse: {err}"));
|
||||||
|
|
||||||
let mut log: EvmLog = stdout.into();
|
let mut log: EvmLog = format!("{stdout}{stderr}").as_str().into();
|
||||||
log.stderr = stderr.into();
|
log.stderr = stderr.into();
|
||||||
if self.bench {
|
if self.bench {
|
||||||
log.parse_gas_used_from_bench();
|
log.parse_gas_used_from_bench();
|
||||||
@@ -558,6 +557,7 @@ allocated bytes: 3711"#;
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore] // https://github.com/ethereum/go-ethereum/issues/30778
|
||||||
fn bench_flipper() {
|
fn bench_flipper() {
|
||||||
let log_runtime = Evm::default()
|
let log_runtime = Evm::default()
|
||||||
.code_blob(EVM_BIN_RUNTIME_FIXTURE.as_bytes().to_vec())
|
.code_blob(EVM_BIN_RUNTIME_FIXTURE.as_bytes().to_vec())
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "revive-explorer"
|
||||||
|
version = "0.1.0"
|
||||||
|
license.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
description = "Helper utility to inspect debug builds"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "revive-explorer"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = { workspace = true }
|
||||||
|
clap = { workspace = true, features = ["help", "std", "derive"] }
|
||||||
|
num_cpus = { workspace = true }
|
||||||
|
|
||||||
|
revive-yul = { workspace = true }
|
||||||
|
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# revive-explorer
|
||||||
|
|
||||||
|
The `revive-explorer` is a helper utility for exploring the compilers YUL lowering unit.
|
||||||
|
|
||||||
|
It analyzes a given shared objects from the debug dump and outputs:
|
||||||
|
- The count of each YUL statement translated.
|
||||||
|
- A per YUL statement break-down of bytecode size contributed per.
|
||||||
|
- Estimated `yul-phaser` cost parameters.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
statements count:
|
||||||
|
block 532
|
||||||
|
Caller 20
|
||||||
|
Not 73
|
||||||
|
Gas 24
|
||||||
|
Shr 2
|
||||||
|
...
|
||||||
|
Shl 259
|
||||||
|
SetImmutable 2
|
||||||
|
CodeSize 1
|
||||||
|
CallDataLoad 87
|
||||||
|
Return 56
|
||||||
|
bytes per statement:
|
||||||
|
Or 756
|
||||||
|
CodeCopy 158
|
||||||
|
Log3 620
|
||||||
|
Return 1562
|
||||||
|
MStore 36128
|
||||||
|
...
|
||||||
|
ReturnDataCopy 2854
|
||||||
|
DataOffset 28
|
||||||
|
assignment 1194
|
||||||
|
Number 540
|
||||||
|
CallValue 4258
|
||||||
|
yul-phaser parameters:
|
||||||
|
--break-cost 1
|
||||||
|
--variable-declaration-cost 3
|
||||||
|
--function-call-cost 8
|
||||||
|
--if-cost 4
|
||||||
|
--expression-statement-cost 6
|
||||||
|
--function-definition-cost 11
|
||||||
|
--switch-cost 3
|
||||||
|
--block-cost 1
|
||||||
|
--leave-cost 1
|
||||||
|
--assignment-cost 1
|
||||||
|
```
|
||||||
|
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
//! The `llvm-dwarfdump` utility helper library.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::{Command, Stdio},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static EXECUTABLE: &str = "llvm-dwarfdump";
|
||||||
|
pub static DEBUG_LINES_ARGUMENTS: [&str; 1] = ["--debug-line"];
|
||||||
|
pub static SOURCE_FILE_ARGUMENTS: [&str; 1] = ["--show-sources"];
|
||||||
|
|
||||||
|
/// Calls the `llvm-dwarfdump` tool to extract debug line information
|
||||||
|
/// from the shared object at `path`. Returns the output.
|
||||||
|
///
|
||||||
|
/// Provide `Some(dwarfdump_exectuable)` to override the default executable.
|
||||||
|
pub fn debug_lines(
|
||||||
|
shared_object: &Path,
|
||||||
|
dwarfdump_executable: &Option<PathBuf>,
|
||||||
|
) -> anyhow::Result<String> {
|
||||||
|
dwarfdump(shared_object, dwarfdump_executable, &DEBUG_LINES_ARGUMENTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls the `llvm-dwarfdump` tool to extract the source file name.
|
||||||
|
/// Returns the source file path.
|
||||||
|
///
|
||||||
|
/// Provide `Some(dwarfdump_exectuable)` to override the default executable.
|
||||||
|
pub fn source_file(
|
||||||
|
shared_object: &Path,
|
||||||
|
dwarfdump_executable: &Option<PathBuf>,
|
||||||
|
) -> anyhow::Result<PathBuf> {
|
||||||
|
let output = dwarfdump(shared_object, dwarfdump_executable, &SOURCE_FILE_ARGUMENTS)?;
|
||||||
|
let output = output.trim();
|
||||||
|
|
||||||
|
if output.is_empty() {
|
||||||
|
anyhow::bail!(
|
||||||
|
"the shared object at path `{}` doesn't contain the source file name. Hint: compile with debug information (-g)?",
|
||||||
|
shared_object.display()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(output.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The internal `llvm-dwarfdump` helper function.
|
||||||
|
fn dwarfdump(
|
||||||
|
shared_object: &Path,
|
||||||
|
dwarfdump_executable: &Option<PathBuf>,
|
||||||
|
arguments: &[&str],
|
||||||
|
) -> anyhow::Result<String> {
|
||||||
|
let executable = dwarfdump_executable
|
||||||
|
.to_owned()
|
||||||
|
.unwrap_or_else(|| PathBuf::from(EXECUTABLE));
|
||||||
|
|
||||||
|
let output = Command::new(executable)
|
||||||
|
.args(arguments)
|
||||||
|
.arg(shared_object)
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()?
|
||||||
|
.wait_with_output()?;
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
anyhow::bail!(String::from_utf8_lossy(&output.stderr).to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(String::from_utf8_lossy(&output.stdout).to_string())
|
||||||
|
}
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
//! The core dwarf dump analyzer library.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
use revive_yul::lexer::token::location::Location;
|
||||||
|
|
||||||
|
use crate::location_mapper::{self, LocationMapper};
|
||||||
|
|
||||||
|
/// The dwarf dump analyzer.
|
||||||
|
///
|
||||||
|
/// Loads debug information from `llvm-dwarfdump` and calculates statistics
|
||||||
|
/// about the compiled YUL statements:
|
||||||
|
/// - Statements count
|
||||||
|
/// - Per-statement
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct DwarfdumpAnalyzer {
|
||||||
|
/// The YUL source file path.
|
||||||
|
source: PathBuf,
|
||||||
|
|
||||||
|
/// The YUL location to statements map.
|
||||||
|
location_map: HashMap<Location, String>,
|
||||||
|
|
||||||
|
/// The `llvm-dwarfdump --debug-lines` output.
|
||||||
|
debug_lines: String,
|
||||||
|
|
||||||
|
/// The observed statements.
|
||||||
|
statements_count: HashMap<String, usize>,
|
||||||
|
/// The observed statement to instructions size.
|
||||||
|
statements_size: HashMap<String, u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DwarfdumpAnalyzer {
|
||||||
|
/// The debug info analyzer constructor.
|
||||||
|
///
|
||||||
|
/// `source` is the path to the YUL source file.
|
||||||
|
/// `debug_lines` is the `llvm-dwarfdump --debug-lines` output.
|
||||||
|
pub fn new(source: &Path, debug_lines: String) -> Self {
|
||||||
|
Self {
|
||||||
|
source: source.to_path_buf(),
|
||||||
|
debug_lines,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run the analysis.
|
||||||
|
pub fn analyze(&mut self) -> anyhow::Result<()> {
|
||||||
|
self.map_locations()?;
|
||||||
|
self.analyze_statements()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Populate the maps so that we can always unwrap later.
|
||||||
|
fn map_locations(&mut self) -> anyhow::Result<()> {
|
||||||
|
self.location_map = LocationMapper::map_locations(&self.source)?;
|
||||||
|
|
||||||
|
self.statements_count = HashMap::with_capacity(self.location_map.len());
|
||||||
|
self.statements_size = HashMap::with_capacity(self.location_map.len());
|
||||||
|
|
||||||
|
for statement in self.location_map.values() {
|
||||||
|
if !self.statements_size.contains_key(statement) {
|
||||||
|
self.statements_size.insert(statement.clone(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
*self.statements_count.entry(statement.clone()).or_insert(0) += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Analyze how much bytes of insturctions each statement contributes.
|
||||||
|
fn analyze_statements(&mut self) -> anyhow::Result<()> {
|
||||||
|
let mut previous_offset = 0;
|
||||||
|
let mut previous_location = Location::new(0, 0);
|
||||||
|
|
||||||
|
for line in self
|
||||||
|
.debug_lines
|
||||||
|
.lines()
|
||||||
|
.skip_while(|line| !line.starts_with("Address"))
|
||||||
|
.skip(2)
|
||||||
|
{
|
||||||
|
let mut parts = line.split_whitespace();
|
||||||
|
let (Some(offset), Some(line), Some(column)) =
|
||||||
|
(parts.next(), parts.next(), parts.next())
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let current_offset = u64::from_str_radix(offset.trim_start_matches("0x"), 16)?;
|
||||||
|
let mut current_location = Location::new(line.parse()?, column.parse()?);
|
||||||
|
|
||||||
|
// TODO: A bug? Needs further investigation.
|
||||||
|
if current_location.line == 0 && current_location.column != 0 {
|
||||||
|
current_location.line = previous_location.line;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(statement) = self.location_map.get(&previous_location) {
|
||||||
|
let contribution = current_offset - previous_offset;
|
||||||
|
*self.statements_size.get_mut(statement).unwrap() += contribution;
|
||||||
|
}
|
||||||
|
|
||||||
|
previous_offset = current_offset;
|
||||||
|
previous_location = current_location;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print the per-statement count break-down.
|
||||||
|
pub fn display_statement_count(&self) {
|
||||||
|
println!("statements count:");
|
||||||
|
for (statement, count) in self.statements_count.iter() {
|
||||||
|
println!("\t{statement} {count}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print the per-statement byte size contribution break-down.
|
||||||
|
pub fn display_statement_size(&self) {
|
||||||
|
println!("bytes per statement:");
|
||||||
|
for (statement, size) in self.statements_size.iter() {
|
||||||
|
println!("\t{statement} {size}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print the estimated `yul-phaser` cost parameters.
|
||||||
|
pub fn display_phaser_costs(&self, yul_phaser_scale: u64) {
|
||||||
|
println!("yul-phaser parameters:");
|
||||||
|
for (parameter, cost) in self.phaser_costs(yul_phaser_scale) {
|
||||||
|
println!("\t{parameter} {cost}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Estimate the `yul-phaser` costs using the simplified weight function:
|
||||||
|
/// `Total size / toal count = cost`
|
||||||
|
pub fn phaser_costs(&self, yul_phaser_scale: u64) -> Vec<(String, u64)> {
|
||||||
|
let mut costs: HashMap<String, (usize, u64)> = HashMap::with_capacity(16);
|
||||||
|
for (statement, count) in self
|
||||||
|
.statements_count
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, count)| **count > 0)
|
||||||
|
{
|
||||||
|
let size = self.statements_size.get(statement).unwrap();
|
||||||
|
let cost = match statement.as_str() {
|
||||||
|
location_mapper::FOR => "--for-loop-cost",
|
||||||
|
location_mapper::OTHER => continue,
|
||||||
|
location_mapper::INTERNAL => continue,
|
||||||
|
location_mapper::BLOCK => "--block-cost",
|
||||||
|
location_mapper::FUNCTION_CALL => "--function-call-cost",
|
||||||
|
location_mapper::IF => "--if-cost",
|
||||||
|
location_mapper::SWITCH => "--switch-cost",
|
||||||
|
location_mapper::DECLARATION => "--variable-declaration-cost",
|
||||||
|
location_mapper::ASSIGNMENT => "--assignment-cost",
|
||||||
|
location_mapper::FUNCTION_DEFINITION => "--function-definition-cost",
|
||||||
|
location_mapper::IDENTIFIER => "--identifier-cost",
|
||||||
|
location_mapper::LITERAL => "--literal-cost",
|
||||||
|
_ => "--expression-statement-cost",
|
||||||
|
};
|
||||||
|
|
||||||
|
let entry = costs.entry(cost.to_string()).or_default();
|
||||||
|
entry.0 += count;
|
||||||
|
entry.1 += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
let costs = costs
|
||||||
|
.iter()
|
||||||
|
.map(|(cost, (count, size))| {
|
||||||
|
let ratio = *size / *count as u64;
|
||||||
|
(cost.to_string(), ratio.min(100))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let scaled_costs = scale_to(
|
||||||
|
costs
|
||||||
|
.iter()
|
||||||
|
.map(|(_, ratio)| *ratio)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.as_slice(),
|
||||||
|
yul_phaser_scale,
|
||||||
|
);
|
||||||
|
|
||||||
|
costs
|
||||||
|
.iter()
|
||||||
|
.zip(scaled_costs)
|
||||||
|
.map(|((cost, _), scaled_ratio)| (cost.to_string(), scaled_ratio))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a slice of u64 values, returns a `Vec<u64>` where each element
|
||||||
|
/// is linearly scaled into the closed interval [1, 10].
|
||||||
|
fn scale_to(data: &[u64], scale_max: u64) -> Vec<u64> {
|
||||||
|
if data.is_empty() {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut min = data[0];
|
||||||
|
let mut max = data[0];
|
||||||
|
for &x in &data[1..] {
|
||||||
|
if x < min {
|
||||||
|
min = x;
|
||||||
|
}
|
||||||
|
if x > max {
|
||||||
|
max = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if max < scale_max {
|
||||||
|
return data.to_vec();
|
||||||
|
}
|
||||||
|
|
||||||
|
let range = max - min;
|
||||||
|
data.iter()
|
||||||
|
.map(|&x| {
|
||||||
|
if range == 0 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
1 + (x - min) * scale_max / range
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
//! The revive explorer leverages debug info to get insights into emitted code.
|
||||||
|
|
||||||
|
pub mod dwarfdump;
|
||||||
|
pub mod dwarfdump_analyzer;
|
||||||
|
pub mod location_mapper;
|
||||||
|
pub mod yul_phaser;
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
//! The location mapper utility maps YUL source locations to AST statements.
|
||||||
|
|
||||||
|
use std::{collections::HashMap, path::Path};
|
||||||
|
|
||||||
|
use revive_yul::{
|
||||||
|
lexer::{token::location::Location, Lexer},
|
||||||
|
parser::{
|
||||||
|
identifier::Identifier,
|
||||||
|
statement::{
|
||||||
|
assignment::Assignment,
|
||||||
|
block::Block,
|
||||||
|
expression::{function_call::FunctionCall, literal::Literal},
|
||||||
|
for_loop::ForLoop,
|
||||||
|
function_definition::FunctionDefinition,
|
||||||
|
if_conditional::IfConditional,
|
||||||
|
object::Object,
|
||||||
|
switch::Switch,
|
||||||
|
variable_declaration::VariableDeclaration,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
visitor::{AstNode, AstVisitor},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Code attributed to an unknown location.
|
||||||
|
pub const OTHER: &str = "other";
|
||||||
|
/// Code attributed to a compiler internal location.
|
||||||
|
pub const INTERNAL: &str = "internal";
|
||||||
|
/// Code attributed to a block.
|
||||||
|
pub const BLOCK: &str = "block";
|
||||||
|
/// Code attributed to a function call.
|
||||||
|
pub const FUNCTION_CALL: &str = "function_call";
|
||||||
|
/// Code attributed to a for loop.
|
||||||
|
pub const FOR: &str = "for";
|
||||||
|
/// Code attributed to an if statement.
|
||||||
|
pub const IF: &str = "if";
|
||||||
|
/// Code attributed to a switch statement.
|
||||||
|
pub const SWITCH: &str = "switch";
|
||||||
|
/// Code attributed to a variable declaration.
|
||||||
|
pub const DECLARATION: &str = "let";
|
||||||
|
/// Code attributed to a variable assignement.
|
||||||
|
pub const ASSIGNMENT: &str = "assignment";
|
||||||
|
/// Code attributed to a function definition.
|
||||||
|
pub const FUNCTION_DEFINITION: &str = "function_definition";
|
||||||
|
/// Code attributed to an identifier.
|
||||||
|
pub const IDENTIFIER: &str = "identifier";
|
||||||
|
/// Code attributed to a literal.
|
||||||
|
pub const LITERAL: &str = "literal";
|
||||||
|
|
||||||
|
/// The location to statements mapper.
|
||||||
|
pub struct LocationMapper(HashMap<Location, String>);
|
||||||
|
|
||||||
|
impl LocationMapper {
|
||||||
|
/// Construct a node location map from the given YUL `source` file.
|
||||||
|
pub fn map_locations(source: &Path) -> anyhow::Result<HashMap<Location, String>> {
|
||||||
|
let mut lexer = Lexer::new(std::fs::read_to_string(source)?);
|
||||||
|
let ast = Object::parse(&mut lexer, None).map_err(|error| {
|
||||||
|
anyhow::anyhow!("Contract `{}` parsing error: {:?}", source.display(), error)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut location_map = Self(Default::default());
|
||||||
|
ast.accept(&mut location_map);
|
||||||
|
location_map.0.insert(Location::new(0, 0), OTHER.into());
|
||||||
|
location_map.0.insert(Location::new(1, 0), INTERNAL.into());
|
||||||
|
|
||||||
|
Ok(location_map.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AstVisitor for LocationMapper {
|
||||||
|
fn visit(&mut self, node: &impl AstNode) {
|
||||||
|
node.visit_children(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_block(&mut self, node: &Block) {
|
||||||
|
node.visit_children(self);
|
||||||
|
self.0.insert(node.location, BLOCK.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_assignment(&mut self, node: &Assignment) {
|
||||||
|
node.visit_children(self);
|
||||||
|
self.0.insert(node.location, ASSIGNMENT.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_if_conditional(&mut self, node: &IfConditional) {
|
||||||
|
node.visit_children(self);
|
||||||
|
self.0.insert(node.location, IF.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_variable_declaration(&mut self, node: &VariableDeclaration) {
|
||||||
|
node.visit_children(self);
|
||||||
|
self.0.insert(node.location, DECLARATION.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_function_call(&mut self, node: &FunctionCall) {
|
||||||
|
node.visit_children(self);
|
||||||
|
self.0.insert(node.location, node.name.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_function_definition(&mut self, node: &FunctionDefinition) {
|
||||||
|
node.visit_children(self);
|
||||||
|
self.0.insert(node.location, FUNCTION_DEFINITION.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_identifier(&mut self, node: &Identifier) {
|
||||||
|
node.visit_children(self);
|
||||||
|
self.0.insert(node.location, IDENTIFIER.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_literal(&mut self, node: &Literal) {
|
||||||
|
node.visit_children(self);
|
||||||
|
self.0.insert(node.location, LITERAL.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_for_loop(&mut self, node: &ForLoop) {
|
||||||
|
node.visit_children(self);
|
||||||
|
self.0.insert(node.location, FOR.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_switch(&mut self, node: &Switch) {
|
||||||
|
node.visit_children(self);
|
||||||
|
self.0.insert(node.location, SWITCH.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
use revive_explorer::{dwarfdump, dwarfdump_analyzer::DwarfdumpAnalyzer, yul_phaser};
|
||||||
|
|
||||||
|
/// The `revive-explorer` is a helper utility for exploring the compilers YUL lowering unit.
|
||||||
|
///
|
||||||
|
/// It analyzes a given shared objects from the debug dump and outputs:
|
||||||
|
/// - The count of each YUL statement translated.
|
||||||
|
/// - A per YUL statement break-down of bytecode size contributed per.
|
||||||
|
/// - Estimated `yul-phaser` cost parameters.
|
||||||
|
///
|
||||||
|
/// Note: This tool might not be fully accurate, especially when the code was optimized.
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
/// Path of the dwarfdump executable.
|
||||||
|
#[arg(short, long)]
|
||||||
|
dwarfdump: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// The YUL phaser cost scale maximum value.
|
||||||
|
#[arg(short, long, default_value_t = 10)]
|
||||||
|
cost_scale: u64,
|
||||||
|
|
||||||
|
/// Run the provided yul-phaser executable using the estimated costs.
|
||||||
|
#[arg(short, long)]
|
||||||
|
yul_phaser: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Path of the shared object to analyze.
|
||||||
|
/// It must have been compiled with debug info (-g).
|
||||||
|
file: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
let args = Args::parse();
|
||||||
|
|
||||||
|
let source_file = dwarfdump::source_file(&args.file, &args.dwarfdump)?;
|
||||||
|
let debug_lines = dwarfdump::debug_lines(&args.file, &args.dwarfdump)?;
|
||||||
|
let mut analyzer = DwarfdumpAnalyzer::new(source_file.as_path(), debug_lines);
|
||||||
|
|
||||||
|
analyzer.analyze()?;
|
||||||
|
|
||||||
|
if let Some(path) = args.yul_phaser.as_ref() {
|
||||||
|
yul_phaser::run(
|
||||||
|
path,
|
||||||
|
source_file.as_path(),
|
||||||
|
analyzer.phaser_costs(args.cost_scale).as_slice(),
|
||||||
|
num_cpus::get() / 2, // TODO: should be configurable.
|
||||||
|
)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
analyzer.display_statement_count();
|
||||||
|
analyzer.display_statement_size();
|
||||||
|
analyzer.display_phaser_costs(args.cost_scale);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
//! The revive explorer YUL phaser utility library.
|
||||||
|
//!
|
||||||
|
//! This can be used to invoke the `yul-phaser` utility,
|
||||||
|
//! used to find better YUL optimizer sequences.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::{Command, Stdio},
|
||||||
|
thread,
|
||||||
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The `yul-phaser` sane default arguments:
|
||||||
|
/// - Less verbose output.
|
||||||
|
/// - Sufficient rounds.
|
||||||
|
/// - Sufficient random population start.
|
||||||
|
const ARGUMENTS: [&str; 6] = [
|
||||||
|
"--hide-round",
|
||||||
|
"--rounds",
|
||||||
|
"1000",
|
||||||
|
"--random-population",
|
||||||
|
"100",
|
||||||
|
"--show-only-top-chromosome",
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Run multiple YUL phaser executables in parallel.
|
||||||
|
pub fn run(
|
||||||
|
executable: &Path,
|
||||||
|
source: &Path,
|
||||||
|
costs: &[(String, u64)],
|
||||||
|
n_threads: usize,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let mut handles = Vec::with_capacity(n_threads);
|
||||||
|
|
||||||
|
for n in 0..n_threads {
|
||||||
|
let executable = executable.to_path_buf();
|
||||||
|
let source = source.to_path_buf();
|
||||||
|
let costs = costs.to_vec();
|
||||||
|
|
||||||
|
handles.push(thread::spawn(move || {
|
||||||
|
spawn_process(executable, source, costs, n)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for handle in handles {
|
||||||
|
let _ = handle.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `yul-phaser` process spawning helper function.
|
||||||
|
fn spawn_process(
|
||||||
|
executable: PathBuf,
|
||||||
|
source: PathBuf,
|
||||||
|
costs: Vec<(String, u64)>,
|
||||||
|
seed: usize,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let cost_parameters = costs
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(parameter, cost)| vec![parameter.clone(), cost.to_string()]);
|
||||||
|
|
||||||
|
let secs = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("Time went backwards")
|
||||||
|
.as_secs();
|
||||||
|
|
||||||
|
Command::new(executable)
|
||||||
|
.args(cost_parameters)
|
||||||
|
.args(ARGUMENTS)
|
||||||
|
.arg("--seed")
|
||||||
|
.arg((seed + secs as usize).to_string())
|
||||||
|
.arg(source)
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.spawn()?
|
||||||
|
.wait()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revive-integration"
|
name = "revive-integration"
|
||||||
version.workspace = true
|
version = "0.3.0"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
@@ -8,19 +8,14 @@ authors.workspace = true
|
|||||||
description = "revive compiler integration test cases"
|
description = "revive compiler integration test cases"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
polkavm = { workspace = true }
|
|
||||||
alloy-primitives = { workspace = true }
|
alloy-primitives = { workspace = true }
|
||||||
alloy-sol-types = { workspace = true }
|
alloy-sol-types = { workspace = true }
|
||||||
hex = { workspace = true }
|
hex = { workspace = true }
|
||||||
env_logger = { workspace = true }
|
|
||||||
log = { workspace = true }
|
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
||||||
revive-solidity = { workspace = true }
|
resolc = { workspace = true }
|
||||||
revive-differential = { workspace = true }
|
|
||||||
revive-llvm-context = { workspace = true }
|
|
||||||
revive-common = { workspace = true }
|
|
||||||
revive-runner = { workspace = true }
|
revive-runner = { workspace = true }
|
||||||
|
revive-llvm-context = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
sha1 = { workspace = true }
|
sha1 = { workspace = true }
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"Baseline": 878,
|
"Baseline": 911,
|
||||||
"Computation": 4305,
|
"Computation": 2337,
|
||||||
"DivisionArithmetics": 39774,
|
"DivisionArithmetics": 14488,
|
||||||
"ERC20": 53405,
|
"ERC20": 17041,
|
||||||
"Events": 1693,
|
"Events": 1672,
|
||||||
"FibonacciIterative": 2917,
|
"FibonacciIterative": 1454,
|
||||||
"Flipper": 3570,
|
"Flipper": 2106,
|
||||||
"SHA1": 32557
|
"SHA1": 7814
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
|
||||||
|
pragma solidity ^0.8;
|
||||||
|
|
||||||
|
/* runner.json
|
||||||
|
{
|
||||||
|
"differential": true,
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"Upload": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "AddModMulMod"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Instantiate": {
|
||||||
|
"value": 123123,
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "AddModMulModTester"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"VerifyCall": {
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
contract AddModMulMod {
|
||||||
|
function test() public returns (uint256) {
|
||||||
|
// Note that this only works because computation on literals is done using
|
||||||
|
// unbounded integers.
|
||||||
|
if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) return 1;
|
||||||
|
if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) return 2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f(uint256 d) public pure returns (uint256) {
|
||||||
|
addmod(1, 2, d);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function g(uint256 d) public pure returns (uint256) {
|
||||||
|
mulmod(1, 2, d);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function h() public pure returns (uint256) {
|
||||||
|
mulmod(0, 1, 2);
|
||||||
|
mulmod(1, 0, 2);
|
||||||
|
addmod(0, 1, 2);
|
||||||
|
addmod(1, 0, 2);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract AddModMulModTester {
|
||||||
|
constructor() payable {
|
||||||
|
AddModMulMod c = new AddModMulMod();
|
||||||
|
|
||||||
|
assert(c.test() == 0);
|
||||||
|
|
||||||
|
try c.f(0) returns (uint m) { revert(); } catch Panic(uint errorCode) {
|
||||||
|
assert(errorCode == 0x12);
|
||||||
|
}
|
||||||
|
|
||||||
|
try c.g(0) returns (uint m) { revert(); } catch Panic(uint errorCode) {
|
||||||
|
assert(errorCode == 0x12);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(c.h() == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.28;
|
||||||
|
|
||||||
|
contract Predicted {
|
||||||
|
uint public salt;
|
||||||
|
|
||||||
|
constructor(uint _salt) {
|
||||||
|
salt = _salt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract AddressPredictor {
|
||||||
|
constructor(uint _salt, bytes memory _bytecode) payable {
|
||||||
|
address deployed = address(new Predicted{salt: bytes32(_salt)}(_salt));
|
||||||
|
address predicted = predictAddress(_salt, _bytecode);
|
||||||
|
assert(deployed == predicted);
|
||||||
|
}
|
||||||
|
|
||||||
|
function predictAddress(
|
||||||
|
uint _foo,
|
||||||
|
bytes memory _bytecode
|
||||||
|
) public view returns (address predicted) {
|
||||||
|
bytes32 addr = keccak256(
|
||||||
|
abi.encodePacked(
|
||||||
|
bytes1(0xff),
|
||||||
|
address(this),
|
||||||
|
bytes32(_foo),
|
||||||
|
keccak256(abi.encodePacked(_bytecode, abi.encode(_foo)))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
predicted = address(uint160(uint(addr)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8;
|
||||||
|
|
||||||
|
/* runner.json
|
||||||
|
{
|
||||||
|
"differential": true,
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"Upload": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "BalanceReceiver"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Instantiate": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "Balance"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"value": 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Call": {
|
||||||
|
"dest": {
|
||||||
|
"Instantiated": 0
|
||||||
|
},
|
||||||
|
"data": "1c8d16b30000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Call": {
|
||||||
|
"dest": {
|
||||||
|
"Instantiated": 0
|
||||||
|
},
|
||||||
|
"data": "6ada15d90000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
contract BalanceReceiver {
|
||||||
|
constructor() payable {}
|
||||||
|
|
||||||
|
fallback() external payable {}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Balance {
|
||||||
|
constructor() payable {
|
||||||
|
// 0 to EOA
|
||||||
|
transfer_to(payable(address(0xdeadbeef)), 0);
|
||||||
|
send_to(payable(address(0xdeadbeef)), 0);
|
||||||
|
|
||||||
|
// 1 to EOA
|
||||||
|
transfer_to(payable(address(0xcafebabe)), 1);
|
||||||
|
send_to(payable(address(0xcafebabe)), 1);
|
||||||
|
|
||||||
|
BalanceReceiver balanceReceiver = new BalanceReceiver();
|
||||||
|
|
||||||
|
// 0 to contract
|
||||||
|
transfer_to(payable(address(balanceReceiver)), 0);
|
||||||
|
send_to(payable(address(balanceReceiver)), 0);
|
||||||
|
|
||||||
|
// 1 to contract
|
||||||
|
transfer_to(payable(address(balanceReceiver)), 1);
|
||||||
|
send_to(payable(address(balanceReceiver)), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function transfer_to(address payable _dest, uint _amount) public payable {
|
||||||
|
_dest.transfer(_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function send_to(address payable _dest, uint _amount) public payable {
|
||||||
|
require(_dest.send(_amount));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.8;
|
||||||
|
|
||||||
|
/* runner.json
|
||||||
|
{
|
||||||
|
"differential": false,
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"Instantiate": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "BaseFee"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"VerifyCall": {
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
contract BaseFee {
|
||||||
|
constructor() payable {
|
||||||
|
assert(block.basefee > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.8;
|
||||||
|
|
||||||
|
/* runner.json
|
||||||
|
{
|
||||||
|
"differential": false,
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"Instantiate": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "BlockHash"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data": "4545454545454545454545454545454545454545454545454545454545454545"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
contract BlockHash {
|
||||||
|
constructor(bytes32 expected) payable {
|
||||||
|
assert(blockhash(0) == expected);
|
||||||
|
assert(blockhash(1) == 0);
|
||||||
|
assert(
|
||||||
|
blockhash(
|
||||||
|
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||||
|
) == 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,19 +2,57 @@
|
|||||||
|
|
||||||
pragma solidity ^0.8;
|
pragma solidity ^0.8;
|
||||||
|
|
||||||
contract Call {
|
/* runner.json
|
||||||
function value_transfer(address payable destination) public payable {
|
{
|
||||||
destination.transfer(msg.value);
|
"differential": true,
|
||||||
}
|
"actions": [
|
||||||
|
{
|
||||||
|
"Upload": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "Callee"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Instantiate": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "Caller"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"value": 123
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Call": {
|
||||||
|
"dest": {
|
||||||
|
"Instantiated": 0
|
||||||
|
},
|
||||||
|
"data": "5a6535fc00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004cafebabe00000000000000000000000000000000000000000000000000000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
contract Callee {
|
||||||
function echo(bytes memory payload) public pure returns (bytes memory) {
|
function echo(bytes memory payload) public pure returns (bytes memory) {
|
||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
function call(
|
receive() external payable {}
|
||||||
address callee,
|
}
|
||||||
bytes memory payload
|
|
||||||
) public pure returns (bytes memory) {
|
contract Caller {
|
||||||
return Call(callee).echo(payload);
|
constructor() payable {
|
||||||
|
Callee callee = new Callee();
|
||||||
|
payable(address(callee)).transfer(msg.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function call(bytes memory payload) public returns (bytes memory) {
|
||||||
|
Callee callee = new Callee();
|
||||||
|
return callee.echo(payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.8;
|
||||||
|
|
||||||
|
// Use a non-zero call gas that works with call gas clipping but not with a truncate.
|
||||||
|
|
||||||
|
/* runner.json
|
||||||
|
{
|
||||||
|
"differential": true,
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"Upload": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "Other"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Instantiate": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "CallGas"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data": "1000000000000000000000000000000000000000000000000000000000000001"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
contract Other {
|
||||||
|
address public last;
|
||||||
|
uint public foo;
|
||||||
|
|
||||||
|
fallback() external {
|
||||||
|
last = msg.sender;
|
||||||
|
foo += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract CallGas {
|
||||||
|
constructor(uint _gas) payable {
|
||||||
|
Other other = new Other();
|
||||||
|
address(other).call{ gas: _gas }(hex"");
|
||||||
|
assert(other.last() == address(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.8;
|
||||||
|
|
||||||
|
/* runner.json
|
||||||
|
{
|
||||||
|
"differential": true,
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"Instantiate": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "Coinbase"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
contract Coinbase {
|
||||||
|
constructor() payable {
|
||||||
|
address coinbase = address(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF);
|
||||||
|
assert(block.coinbase == coinbase);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,14 @@ pragma solidity ^0.8;
|
|||||||
},
|
},
|
||||||
"data": "fc9c8d39"
|
"data": "fc9c8d39"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Call": {
|
||||||
|
"dest": {
|
||||||
|
"Instantiated": 0
|
||||||
|
},
|
||||||
|
"data": "3af973b1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -43,4 +51,12 @@ contract Context {
|
|||||||
function caller() public view returns (address ret) {
|
function caller() public view returns (address ret) {
|
||||||
ret = msg.sender;
|
ret = msg.sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function chain_id() public view returns (uint) {
|
||||||
|
uint256 id;
|
||||||
|
assembly {
|
||||||
|
id := chainid()
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.8.31;
|
||||||
|
|
||||||
|
/* runner.json
|
||||||
|
{
|
||||||
|
"differential": true,
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"Instantiate": {
|
||||||
|
"code": {
|
||||||
|
"Solidity": {
|
||||||
|
"contract": "CountLeadingZeros"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// The EIP-7939 test vectors:
|
||||||
|
/// https://eips.ethereum.org/EIPS/eip-7939#test-cases
|
||||||
|
contract CountLeadingZeros {
|
||||||
|
function clz(uint256 x) internal pure returns (uint256 r) {
|
||||||
|
assembly {
|
||||||
|
r := clz(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() payable {
|
||||||
|
assert(
|
||||||
|
clz(0x000000000000000000000000000000000000000000000000000000000000000)
|
||||||
|
== 0x0000000000000000000000000000000000000000000000000000000000000100
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
clz(0x8000000000000000000000000000000000000000000000000000000000000000)
|
||||||
|
== 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
clz(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
|
||||||
|
== 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
clz(0x4000000000000000000000000000000000000000000000000000000000000000)
|
||||||
|
== 0x0000000000000000000000000000000000000000000000000000000000000001
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
clz(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
|
||||||
|
== 0x0000000000000000000000000000000000000000000000000000000000000001
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
clz(0x0000000000000000000000000000000000000000000000000000000000000001)
|
||||||
|
== 0x00000000000000000000000000000000000000000000000000000000000000ff
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user