mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-14 18:01:05 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ab11a09a9 | |||
| c952471647 |
@@ -20,22 +20,27 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
if [ -z "${{ inputs.version }}" ]; then
|
if [ -z "${{ inputs.version }}" ]; then
|
||||||
# Get the full version from the LLVM submodule.
|
# Extract branch from .gitmodules (e.g., "release/18.x")
|
||||||
git submodule update --init --recursive
|
BRANCH=$(git config -f .gitmodules submodule.llvm.branch)
|
||||||
cd llvm
|
if [ -n "$BRANCH" ]; then
|
||||||
# Try to get the most recent tag (e.g., "llvmorg-18.1.8")
|
# Extract version from branch name (e.g., "18.x" from "release/18.x")
|
||||||
LLVM_TAG=$(git describe --tags --abbrev=0 2>/dev/null)
|
VERSION_PREFIX=$(echo "$BRANCH" | sed 's|release/||' | sed 's|\.x$||')
|
||||||
if [ -n "$LLVM_TAG" ]; then
|
echo "Detected LLVM version prefix from submodule branch: $VERSION_PREFIX"
|
||||||
echo "Detected LLVM version from submodule: $LLVM_TAG"
|
|
||||||
# Convert "llvmorg-x.y.z" to "llvm-x.y.z"
|
# Special case: pin LLVM 18 to specific version 18.1.8
|
||||||
LLVM_VERSION=$(echo "$LLVM_TAG" | sed 's/^llvmorg-/llvm-/')
|
if [ "$VERSION_PREFIX" = "18" ]; then
|
||||||
echo "Detected LLVM version from submodule: $LLVM_VERSION"
|
echo "Using pinned version for LLVM 18: llvm-18.1.8"
|
||||||
echo "version_prefix=$LLVM_VERSION" >> $GITHUB_OUTPUT
|
echo "version_prefix=llvm-18.1.8" >> $GITHUB_OUTPUT
|
||||||
|
elif [ "$VERSION_PREFIX" = "21" ]; then
|
||||||
|
echo "Using pinned version for LLVM 21: llvm-21.1.6"
|
||||||
|
echo "version_prefix=llvm-21.1.6" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "version_prefix=llvm-$VERSION_PREFIX" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "Could not detect LLVM version from submodule, will use latest release"
|
echo "No branch found in .gitmodules, will use latest release"
|
||||||
echo "version_prefix=" >> $GITHUB_OUTPUT
|
echo "version_prefix=" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
cd ..
|
|
||||||
else
|
else
|
||||||
echo "Using explicitly provided version: ${{ inputs.version }}"
|
echo "Using explicitly provided version: ${{ inputs.version }}"
|
||||||
echo "version_prefix=${{ inputs.version }}" >> $GITHUB_OUTPUT
|
echo "version_prefix=${{ inputs.version }}" >> $GITHUB_OUTPUT
|
||||||
@@ -51,12 +56,15 @@ runs:
|
|||||||
result-encoding: string
|
result-encoding: string
|
||||||
script: |
|
script: |
|
||||||
let page = 1;
|
let page = 1;
|
||||||
let releases = [];
|
let allReleases = [];
|
||||||
|
|
||||||
let target = process.env.target
|
let target = process.env.target
|
||||||
let versionPrefix = process.env.version_prefix
|
let versionPrefix = process.env.version_prefix
|
||||||
|
|
||||||
do {
|
// Fetch all releases from all pages
|
||||||
|
core.info('Fetching releases from revive repository...');
|
||||||
|
let hasMorePages = true;
|
||||||
|
while (hasMorePages) {
|
||||||
const res = await github.rest.repos.listReleases({
|
const res = await github.rest.repos.listReleases({
|
||||||
owner: context.repo.owner,
|
owner: context.repo.owner,
|
||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
@@ -64,43 +72,57 @@ runs:
|
|||||||
page,
|
page,
|
||||||
});
|
});
|
||||||
|
|
||||||
releases = res.data
|
if (res.data.length > 0) {
|
||||||
releases.sort((a, b) => {
|
core.info(`Page ${page}: Fetched ${res.data.length} releases`);
|
||||||
return (a.published_at < b.published_at) ? 1 : ((a.published_at > b.published_at) ? -1 : 0);
|
allReleases.push(...res.data);
|
||||||
});
|
page++;
|
||||||
|
|
||||||
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 {
|
} else {
|
||||||
// Find latest LLVM release
|
hasMorePages = false;
|
||||||
llvmRelease = releases.find(release => {
|
|
||||||
return release.tag_name.startsWith('llvm-');
|
|
||||||
});
|
|
||||||
if (llvmRelease) {
|
|
||||||
core.info(`Found latest LLVM version: ${llvmRelease.tag_name}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (llvmRelease){
|
core.info(`Total releases fetched: ${allReleases.length}`);
|
||||||
let asset = llvmRelease.assets.find(asset =>{
|
|
||||||
return asset.name.includes(target);
|
// Sort all releases by publication date (newest first)
|
||||||
});
|
allReleases.sort((a, b) => {
|
||||||
if (!asset){
|
return (a.published_at < b.published_at) ? 1 : ((a.published_at > b.published_at) ? -1 : 0);
|
||||||
core.setFailed(`Artifact for '${target}' not found in release ${llvmRelease.tag_name} (${llvmRelease.html_url})`);
|
});
|
||||||
process.exit();
|
|
||||||
}
|
// Debug: Print all LLVM releases
|
||||||
return asset.browser_download_url;
|
const llvmReleases = allReleases.filter(r => r.tag_name.startsWith('llvm-'));
|
||||||
|
core.info(`Found ${llvmReleases.length} LLVM releases in total:`);
|
||||||
|
llvmReleases.forEach(r => {
|
||||||
|
core.info(` - ${r.tag_name} (published: ${r.published_at})`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find the appropriate LLVM release
|
||||||
|
let llvmRelease;
|
||||||
|
if (versionPrefix) {
|
||||||
|
// Search for latest release matching the version prefix
|
||||||
|
llvmRelease = llvmReleases.find(release => {
|
||||||
|
return release.tag_name.startsWith(versionPrefix);
|
||||||
|
});
|
||||||
|
if (llvmRelease) {
|
||||||
|
core.info(`Selected LLVM release matching prefix '${versionPrefix}': ${llvmRelease.tag_name}`);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Find latest LLVM release (first in sorted list)
|
||||||
|
llvmRelease = llvmReleases[0];
|
||||||
|
if (llvmRelease) {
|
||||||
|
core.info(`Selected latest LLVM version: ${llvmRelease.tag_name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
page++;
|
if (llvmRelease) {
|
||||||
} while(releases.length > 0);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
if (versionPrefix) {
|
if (versionPrefix) {
|
||||||
core.setFailed(`No LLVM releases matching prefix '${versionPrefix}' found! Please check the version.`);
|
core.setFailed(`No LLVM releases matching prefix '${versionPrefix}' found! Please check the version.`);
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ jobs:
|
|||||||
cat /etc/apt/sources.list
|
cat /etc/apt/sources.list
|
||||||
sudo sed -i 's/jammy/noble/g' /etc/apt/sources.list
|
sudo sed -i 's/jammy/noble/g' /etc/apt/sources.list
|
||||||
cat /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
|
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 g++ build-essential linux-libc-dev
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
if: ${{ matrix.host == 'macos' }}
|
if: ${{ matrix.host == 'macos' }}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ jobs:
|
|||||||
if: ${{ matrix.type == 'native' }}
|
if: ${{ matrix.type == 'native' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
export LLVM_SYS_181_PREFIX=$PWD/llvm-${{ matrix.target }}
|
export LLVM_SYS_211_PREFIX=$PWD/llvm-${{ matrix.target }}
|
||||||
make install-bin
|
make install-bin
|
||||||
mv target/release/resolc resolc-${{ matrix.target }} || mv target/release/resolc.exe resolc-${{ matrix.target }}.exe
|
mv target/release/resolc resolc-${{ matrix.target }} || mv target/release/resolc.exe resolc-${{ matrix.target }}.exe
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ jobs:
|
|||||||
cd /opt/revive
|
cd /opt/revive
|
||||||
chown -R root:root .
|
chown -R root:root .
|
||||||
apt update && apt upgrade -y && apt install -y pkg-config
|
apt update && apt upgrade -y && apt install -y pkg-config
|
||||||
export LLVM_SYS_181_PREFIX=/opt/revive/llvm-${{ matrix.target }}
|
export LLVM_SYS_211_PREFIX=/opt/revive/llvm-${{ matrix.target }}
|
||||||
make install-bin
|
make install-bin
|
||||||
mv target/${{ matrix.target }}/release/resolc resolc-${{ matrix.target }}
|
mv target/${{ matrix.target }}/release/resolc resolc-${{ matrix.target }}
|
||||||
"
|
"
|
||||||
@@ -163,7 +163,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
export LLVM_SYS_181_PREFIX=$PWD/llvm-x86_64-unknown-linux-gnu
|
export LLVM_SYS_211_PREFIX=$PWD/llvm-x86_64-unknown-linux-gnu
|
||||||
export REVIVE_LLVM_TARGET_PREFIX=$PWD/llvm-wasm32-unknown-emscripten
|
export REVIVE_LLVM_TARGET_PREFIX=$PWD/llvm-wasm32-unknown-emscripten
|
||||||
source emsdk/emsdk_env.sh
|
source emsdk/emsdk_env.sh
|
||||||
make install-wasm
|
make install-wasm
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Set LLVM Environment Variables
|
- name: Set LLVM Environment Variables
|
||||||
run: |
|
run: |
|
||||||
echo "LLVM_SYS_181_PREFIX=$(pwd)/llvm-x86_64-unknown-linux-gnu" >> $GITHUB_ENV
|
echo "LLVM_SYS_211_PREFIX=$(pwd)/llvm-x86_64-unknown-linux-gnu" >> $GITHUB_ENV
|
||||||
echo "REVIVE_LLVM_TARGET_PREFIX=$(pwd)/llvm-wasm32-unknown-emscripten" >> $GITHUB_ENV
|
echo "REVIVE_LLVM_TARGET_PREFIX=$(pwd)/llvm-wasm32-unknown-emscripten" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Build Revive
|
- name: Build Revive
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Set LLVM Environment Variables
|
- name: Set LLVM Environment Variables
|
||||||
run: |
|
run: |
|
||||||
echo "LLVM_SYS_181_PREFIX=$(pwd)/llvm-x86_64-unknown-linux-gnu" >> $GITHUB_ENV
|
echo "LLVM_SYS_211_PREFIX=$(pwd)/llvm-x86_64-unknown-linux-gnu" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Install Geth
|
- name: Install Geth
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
[submodule "llvm"]
|
[submodule "llvm"]
|
||||||
path = llvm
|
path = llvm
|
||||||
url = https://github.com/llvm/llvm-project.git
|
url = https://github.com/llvm/llvm-project.git
|
||||||
branch = release/18.x
|
branch = release/21.x
|
||||||
|
|||||||
@@ -16,12 +16,10 @@ Supported `polkadot-sdk` rev: `unstable2507`
|
|||||||
- Standard JSON mode: Don't forward EVM bytecode related output selections to solc.
|
- Standard JSON mode: Don't forward EVM bytecode related output selections to solc.
|
||||||
- The supported `polkadot-sdk` release is `unstable2507`.
|
- The supported `polkadot-sdk` release is `unstable2507`.
|
||||||
- The `INVALID` opcode and OOB memory accesses now consume all remaining gas.
|
- 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:
|
### Fixed:
|
||||||
- The missing `STOP` instruction at the end of `code` blocks.
|
- The missing `STOP` instruction at the end of `code` blocks.
|
||||||
- The missing bounds check in the internal sbrk implementation.
|
- The missing bounds check in the internal sbrk implementation.
|
||||||
- The call gas is no longer ignored.
|
|
||||||
|
|
||||||
## v0.5.0
|
## v0.5.0
|
||||||
|
|
||||||
|
|||||||
Generated
+7
-10
@@ -4552,24 +4552,21 @@ checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inkwell"
|
name = "inkwell"
|
||||||
version = "0.6.0"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/TheDan64/inkwell.git?branch=master#7abf48a2cf4fc3e338fbeb1780ac7507cbb13e98"
|
||||||
checksum = "e67349bd7578d4afebbe15eaa642a80b884e8623db74b1716611b131feb1deef"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
|
||||||
"inkwell_internals",
|
"inkwell_internals",
|
||||||
"libc",
|
"libc",
|
||||||
"llvm-sys",
|
"llvm-sys",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror 1.0.69",
|
"thiserror 2.0.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inkwell_internals"
|
name = "inkwell_internals"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/TheDan64/inkwell.git?branch=master#7abf48a2cf4fc3e338fbeb1780ac7507cbb13e98"
|
||||||
checksum = "f365c8de536236cfdebd0ba2130de22acefed18b1fb99c32783b3840aec5fb46"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -4927,9 +4924,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "llvm-sys"
|
name = "llvm-sys"
|
||||||
version = "181.2.0"
|
version = "211.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d320f9d2723c97d4b78f9190a61ed25cc7cfbe456668c08e6e7dd8e50ceb8500"
|
checksum = "108b3ad2b2eaf2a561fc74196273b20e3436e4a688b8b44e250d83974dc1b2e2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cc",
|
"cc",
|
||||||
|
|||||||
+3
-2
@@ -80,9 +80,10 @@ polkadot-sdk = { version = "=2507.4.0" }
|
|||||||
|
|
||||||
# llvm
|
# llvm
|
||||||
[workspace.dependencies.inkwell]
|
[workspace.dependencies.inkwell]
|
||||||
version = "0.6.0"
|
git = "https://github.com/TheDan64/inkwell.git"
|
||||||
|
branch = "master"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["serde", "llvm18-1", "no-libffi-linking", "target-riscv"]
|
features = ["serde", "llvm21-1", "no-libffi-linking", "target-riscv"]
|
||||||
|
|
||||||
[profile.bench]
|
[profile.bench]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ RUN apt update && \
|
|||||||
COPY . .
|
COPY . .
|
||||||
COPY --from=llvm-builder /opt/revive/target-llvm /opt/revive/target-llvm
|
COPY --from=llvm-builder /opt/revive/target-llvm /opt/revive/target-llvm
|
||||||
|
|
||||||
ENV LLVM_SYS_181_PREFIX=/opt/revive/target-llvm/musl/target-final
|
ENV LLVM_SYS_211_PREFIX=/opt/revive/target-llvm/musl/target-final
|
||||||
RUN make install-bin
|
RUN make install-bin
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
|||||||
@@ -69,9 +69,9 @@ Download the [latest LLVM build](https://github.com/paritytech/revive/releases?q
|
|||||||
> xattr -rc </path/to/the/extracted/archive>/target-llvm/gnu/target-final/bin/*
|
> xattr -rc </path/to/the/extracted/archive>/target-llvm/gnu/target-final/bin/*
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
After extracting the archive, point `$LLVM_SYS_181_PREFIX` to it:
|
After extracting the archive, point `$LLVM_SYS_211_PREFIX` to it:
|
||||||
```sh
|
```sh
|
||||||
export LLVM_SYS_181_PREFIX=</path/to/the/extracted/archive>/target-llvm/gnu/target-final
|
export LLVM_SYS_211_PREFIX=</path/to/the/extracted/archive>/target-llvm/gnu/target-final
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
@@ -79,18 +79,18 @@ export LLVM_SYS_181_PREFIX=</path/to/the/extracted/archive>/target-llvm/gnu/targ
|
|||||||
<details>
|
<details>
|
||||||
<summary>Building from source</summary>
|
<summary>Building from source</summary>
|
||||||
|
|
||||||
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:
|
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_211_PREFIX` to the installation afterwards:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
make install-llvm
|
make install-llvm
|
||||||
export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final
|
export LLVM_SYS_211_PREFIX=${PWD}/target-llvm/gnu/target-final
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### The `resolc` Solidity frontend
|
### The `resolc` Solidity frontend
|
||||||
|
|
||||||
To build the `resolc` Solidity frontend executable, make sure you have obtained a compatible LLVM build and did export the `LLVM_SYS_181_PREFIX` environment variable pointing to it (see [above](#LLVM)).
|
To build the `resolc` Solidity frontend executable, make sure you have obtained a compatible LLVM build and did export the `LLVM_SYS_211_PREFIX` environment variable pointing to it (see [above](#LLVM)).
|
||||||
|
|
||||||
To install the `resolc` Solidity frontend executable:
|
To install the `resolc` Solidity frontend executable:
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! The compiler build utilities library.
|
//! The compiler build utilities library.
|
||||||
|
|
||||||
/// The revive LLVM host dependency directory prefix environment variable.
|
/// The revive LLVM host dependency directory prefix environment variable.
|
||||||
pub const REVIVE_LLVM_HOST_PREFIX: &str = "LLVM_SYS_181_PREFIX";
|
pub const REVIVE_LLVM_HOST_PREFIX: &str = "LLVM_SYS_211_PREFIX";
|
||||||
|
|
||||||
/// The revive LLVM target dependency directory prefix environment variable.
|
/// The revive LLVM target dependency directory prefix environment variable.
|
||||||
pub const REVIVE_LLVM_TARGET_PREFIX: &str = "REVIVE_LLVM_TARGET_PREFIX";
|
pub const REVIVE_LLVM_TARGET_PREFIX: &str = "REVIVE_LLVM_TARGET_PREFIX";
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
// 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,7 +20,7 @@ pragma solidity ^0.8.28;
|
|||||||
"dest": {
|
"dest": {
|
||||||
"Instantiated": 0
|
"Instantiated": 0
|
||||||
},
|
},
|
||||||
"data": "0be0e4a60000000000000000000000000000000000000000000000000000000000000000"
|
"data": "e2179b8e"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -29,12 +29,12 @@ pragma solidity ^0.8.28;
|
|||||||
|
|
||||||
contract MLoad {
|
contract MLoad {
|
||||||
constructor() payable {
|
constructor() payable {
|
||||||
assert(loadAt(0) == 0);
|
assert(g() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadAt(uint _offset) public payable returns (uint m) {
|
function g() public payable returns (uint m) {
|
||||||
assembly {
|
assembly {
|
||||||
m := mload(_offset)
|
m := mload(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ pragma solidity ^0.8;
|
|||||||
"data": "1c8d16b30000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
|
"data": "1c8d16b30000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"VerifyCall": {
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Call": {
|
"Call": {
|
||||||
"dest": {
|
"dest": {
|
||||||
@@ -31,6 +36,11 @@ pragma solidity ^0.8;
|
|||||||
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000001"
|
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000001"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"VerifyCall": {
|
||||||
|
"success": false
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Call": {
|
"Call": {
|
||||||
"dest": {
|
"dest": {
|
||||||
@@ -38,6 +48,11 @@ pragma solidity ^0.8;
|
|||||||
},
|
},
|
||||||
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000000"
|
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000000"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"VerifyCall": {
|
||||||
|
"success": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ pragma solidity ^0.8;
|
|||||||
"data": "1c8d16b30000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
|
"data": "1c8d16b30000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"VerifyCall": {
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Call": {
|
"Call": {
|
||||||
"dest": {
|
"dest": {
|
||||||
@@ -31,6 +36,11 @@ pragma solidity ^0.8;
|
|||||||
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000001"
|
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000001"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"VerifyCall": {
|
||||||
|
"success": false
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Call": {
|
"Call": {
|
||||||
"dest": {
|
"dest": {
|
||||||
@@ -38,6 +48,11 @@ pragma solidity ^0.8;
|
|||||||
},
|
},
|
||||||
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000000"
|
"data": "fb9e8d050000000000000000000000000000000000000000000000000000000000000000"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"VerifyCall": {
|
||||||
|
"success": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,15 +234,6 @@ sol!(
|
|||||||
);
|
);
|
||||||
case!("MCopy.sol", MCopy, memcpyCall, memcpy, payload: Bytes);
|
case!("MCopy.sol", MCopy, memcpyCall, memcpy, payload: Bytes);
|
||||||
|
|
||||||
sol!(
|
|
||||||
contract MLoad {
|
|
||||||
constructor() payable;
|
|
||||||
|
|
||||||
function loadAt(uint _offset) public payable returns (uint m);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
case!("MLoad.sol", MLoad, loadAtCall, load_at, _offset: U256);
|
|
||||||
|
|
||||||
sol!(
|
sol!(
|
||||||
contract Call {
|
contract Call {
|
||||||
function value_transfer(address payable destination) public payable;
|
function value_transfer(address payable destination) public payable;
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ test_spec!(add_mod_mul_mod, "AddModMulModTester", "AddModMulMod.sol");
|
|||||||
test_spec!(memory_bounds, "MemoryBounds", "MemoryBounds.sol");
|
test_spec!(memory_bounds, "MemoryBounds", "MemoryBounds.sol");
|
||||||
test_spec!(selfdestruct, "Selfdestruct", "Selfdestruct.sol");
|
test_spec!(selfdestruct, "Selfdestruct", "Selfdestruct.sol");
|
||||||
test_spec!(clz, "CountLeadingZeros", "CountLeadingZeros.sol");
|
test_spec!(clz, "CountLeadingZeros", "CountLeadingZeros.sol");
|
||||||
test_spec!(call_gas, "CallGas", "CallGas.sol");
|
|
||||||
|
|
||||||
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
|
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
|
||||||
vec![Instantiate {
|
vec![Instantiate {
|
||||||
@@ -452,6 +451,50 @@ fn ext_code_size() {
|
|||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "ReentranceDenied")]
|
||||||
|
fn send_denies_reentrancy() {
|
||||||
|
let value = 1000;
|
||||||
|
Specs {
|
||||||
|
actions: vec![
|
||||||
|
instantiate("contracts/Send.sol", "Send").remove(0),
|
||||||
|
Call {
|
||||||
|
origin: TestAddress::Alice,
|
||||||
|
dest: TestAddress::Instantiated(0),
|
||||||
|
value,
|
||||||
|
gas_limit: None,
|
||||||
|
storage_deposit_limit: None,
|
||||||
|
data: Contract::send_self(U256::from(value)).calldata,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
differential: false,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "ReentranceDenied")]
|
||||||
|
fn transfer_denies_reentrancy() {
|
||||||
|
let value = 1000;
|
||||||
|
Specs {
|
||||||
|
actions: vec![
|
||||||
|
instantiate("contracts/Transfer.sol", "Transfer").remove(0),
|
||||||
|
Call {
|
||||||
|
origin: TestAddress::Alice,
|
||||||
|
dest: TestAddress::Instantiated(0),
|
||||||
|
value,
|
||||||
|
gas_limit: None,
|
||||||
|
storage_deposit_limit: None,
|
||||||
|
data: Contract::transfer_self(U256::from(value)).calldata,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
differential: false,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create2_salt() {
|
fn create2_salt() {
|
||||||
let salt = U256::from(777);
|
let salt = U256::from(777);
|
||||||
@@ -671,41 +714,3 @@ fn invalid_opcode_works() {
|
|||||||
|
|
||||||
assert_eq!(result.weight_consumed, GAS_LIMIT);
|
assert_eq!(result.weight_consumed, GAS_LIMIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load from heap memory using an out of bounds offset and expect the
|
|
||||||
/// contract to hit the `invalid` syscall to use all gas (like on EVM).
|
|
||||||
///
|
|
||||||
/// The offset is picked such that a regular truncate would be in bounds.
|
|
||||||
#[test]
|
|
||||||
fn safe_truncate_int_to_xlen_works() {
|
|
||||||
let offset = 0x10000000_00000000u64;
|
|
||||||
let data = Contract::load_at(Uint::from(offset)).calldata;
|
|
||||||
let mut actions = instantiate("contracts/MLoad.sol", "MLoad");
|
|
||||||
actions.append(&mut vec![
|
|
||||||
Call {
|
|
||||||
origin: TestAddress::Alice,
|
|
||||||
dest: TestAddress::Instantiated(0),
|
|
||||||
value: 0,
|
|
||||||
gas_limit: None,
|
|
||||||
storage_deposit_limit: None,
|
|
||||||
data,
|
|
||||||
},
|
|
||||||
VerifyCall(VerifyCallExpectation {
|
|
||||||
success: false,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
let results = Specs {
|
|
||||||
actions,
|
|
||||||
differential: true,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.run();
|
|
||||||
|
|
||||||
let CallResult::Exec { result, .. } = results.last().unwrap() else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(result.weight_consumed, GAS_LIMIT);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ fn set_rustc_link_flags() {
|
|||||||
"lldELF",
|
"lldELF",
|
||||||
"lldCommon",
|
"lldCommon",
|
||||||
"lldMachO",
|
"lldMachO",
|
||||||
|
"lldWasm",
|
||||||
"LLVMSupport",
|
"LLVMSupport",
|
||||||
"LLVMLinker",
|
"LLVMLinker",
|
||||||
"LLVMCore",
|
"LLVMCore",
|
||||||
@@ -68,6 +69,33 @@ fn set_rustc_link_flags() {
|
|||||||
"LLVMBitReader",
|
"LLVMBitReader",
|
||||||
"LLVMRemarks",
|
"LLVMRemarks",
|
||||||
"LLVMBitstreamReader",
|
"LLVMBitstreamReader",
|
||||||
|
"LLVMTextAPI",
|
||||||
|
"LLVMDebugInfoDWARFLowLevel",
|
||||||
|
"LLVMDebugInfoGSYM",
|
||||||
|
"LLVMDebugInfoMSF",
|
||||||
|
"LLVMDebugInfoPDB",
|
||||||
|
"LLVMDebugInfoBTF",
|
||||||
|
"LLVMInterfaceStub",
|
||||||
|
"LLVMCGData",
|
||||||
|
"LLVMMIRParser",
|
||||||
|
"LLVMDWARFLinker",
|
||||||
|
"LLVMDWARFLinkerParallel",
|
||||||
|
"LLVMDWARFLinkerClassic",
|
||||||
|
"LLVMLibDriver",
|
||||||
|
"LLVMDlltoolDriver",
|
||||||
|
"LLVMTextAPIBinaryReader",
|
||||||
|
"LLVMCoverage",
|
||||||
|
"LLVMLineEditor",
|
||||||
|
"LLVMRISCVTargetMCA",
|
||||||
|
"LLVMRuntimeDyld",
|
||||||
|
"LLVMDWP",
|
||||||
|
"LLVMDWARFCFIChecker",
|
||||||
|
"LLVMDebugInfoLogicalView",
|
||||||
|
"LLVMMCA",
|
||||||
|
"LLVMipo",
|
||||||
|
"LLVMVectorize",
|
||||||
|
"LLVMSandboxIR",
|
||||||
|
"LLVMExtensions",
|
||||||
] {
|
] {
|
||||||
println!("cargo:rustc-link-lib=static={lib}");
|
println!("cargo:rustc-link-lib=static={lib}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,10 +82,10 @@ Obtain a compatible build for your host platform from the release section of thi
|
|||||||
|
|
||||||
Build artifacts end up in the `./target-llvm/gnu/target-final/` directory by default.
|
Build artifacts end up in the `./target-llvm/gnu/target-final/` directory by default.
|
||||||
The `gnu` directory depends on the supported archticture and will either be `gnu`, `musl` or `emscripten`.
|
The `gnu` directory depends on the supported archticture and will either be `gnu`, `musl` or `emscripten`.
|
||||||
You now need to export the final target directory `$LLVM_SYS_181_PREFIX`:
|
You now need to export the final target directory `$LLVM_SYS_211_PREFIX`:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final
|
export LLVM_SYS_211_PREFIX=${PWD}/target-llvm/gnu/target-final
|
||||||
```
|
```
|
||||||
|
|
||||||
If built with the `--enable-tests` option, test tools will be in the `./target-llvm/gnu/build-final/` directory, along with copies of the build artifacts. For all supported build options, run `revive-llvm build --help`.
|
If built with the `--enable-tests` option, test tools will be in the `./target-llvm/gnu/build-final/` directory, along with copies of the build artifacts. For all supported build options, run `revive-llvm build --help`.
|
||||||
|
|||||||
@@ -221,7 +221,6 @@ fn build_host(
|
|||||||
"-DCOMPILER_RT_BUILD_MEMPROF='Off'",
|
"-DCOMPILER_RT_BUILD_MEMPROF='Off'",
|
||||||
"-DCOMPILER_RT_BUILD_ORC='Off'",
|
"-DCOMPILER_RT_BUILD_ORC='Off'",
|
||||||
"-DCOMPILER_RT_DEFAULT_TARGET_ARCH='aarch64'",
|
"-DCOMPILER_RT_DEFAULT_TARGET_ARCH='aarch64'",
|
||||||
"-DCOMPILER_RT_DEFAULT_TARGET_ONLY='On'",
|
|
||||||
])
|
])
|
||||||
.args(crate::platforms::shared::SHARED_BUILD_OPTS)
|
.args(crate::platforms::shared::SHARED_BUILD_OPTS)
|
||||||
.args(crate::platforms::shared::shared_build_opts_ccache(
|
.args(crate::platforms::shared::shared_build_opts_ccache(
|
||||||
|
|||||||
@@ -219,7 +219,6 @@ fn build_host(
|
|||||||
"-DCOMPILER_RT_BUILD_MEMPROF='Off'",
|
"-DCOMPILER_RT_BUILD_MEMPROF='Off'",
|
||||||
"-DCOMPILER_RT_BUILD_ORC='Off'",
|
"-DCOMPILER_RT_BUILD_ORC='Off'",
|
||||||
"-DCOMPILER_RT_DEFAULT_TARGET_ARCH='x86_64'",
|
"-DCOMPILER_RT_DEFAULT_TARGET_ARCH='x86_64'",
|
||||||
"-DCOMPILER_RT_DEFAULT_TARGET_ONLY='On'",
|
|
||||||
"-DLIBCLANG_BUILD_STATIC='On'",
|
"-DLIBCLANG_BUILD_STATIC='On'",
|
||||||
"-DBUILD_SHARED_LIBS='Off'",
|
"-DBUILD_SHARED_LIBS='Off'",
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ pub const MUSL_SNAPSHOTS_URL: &str = "https://git.musl-libc.org/cgit/musl/snapsh
|
|||||||
pub const EMSDK_SOURCE_URL: &str = "https://github.com/emscripten-core/emsdk.git";
|
pub const EMSDK_SOURCE_URL: &str = "https://github.com/emscripten-core/emsdk.git";
|
||||||
|
|
||||||
/// The emscripten SDK version.
|
/// The emscripten SDK version.
|
||||||
pub const EMSDK_VERSION: &str = "4.0.9";
|
pub const EMSDK_VERSION: &str = "4.0.20";
|
||||||
|
|
||||||
/// The subprocess runner.
|
/// The subprocess runner.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use serde::Serialize;
|
|||||||
/// inside of the LLVM build directory. This order is actually generated during the building.
|
/// inside of the LLVM build directory. This order is actually generated during the building.
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum Attribute {
|
pub enum Attribute {
|
||||||
Unused = 0,
|
// FirstEnumAttr = 1,
|
||||||
AllocAlign = 1,
|
AllocAlign = 1,
|
||||||
AllocatedPointer = 2,
|
AllocatedPointer = 2,
|
||||||
AlwaysInline = 3,
|
AlwaysInline = 3,
|
||||||
@@ -16,92 +16,107 @@ pub enum Attribute {
|
|||||||
Cold = 5,
|
Cold = 5,
|
||||||
Convergent = 6,
|
Convergent = 6,
|
||||||
CoroDestroyOnlyWhenComplete = 7,
|
CoroDestroyOnlyWhenComplete = 7,
|
||||||
DeadOnUnwind = 8,
|
CoroElideSafe = 8,
|
||||||
DisableSanitizerInstrumentation = 9,
|
DeadOnReturn = 9,
|
||||||
FnRetThunkExtern = 10,
|
DeadOnUnwind = 10,
|
||||||
Hot = 11,
|
DisableSanitizerInstrumentation = 11,
|
||||||
ImmArg = 12,
|
FnRetThunkExtern = 12,
|
||||||
InReg = 13,
|
Hot = 13,
|
||||||
InlineHint = 14,
|
HybridPatchable = 14,
|
||||||
JumpTable = 15,
|
ImmArg = 15,
|
||||||
MinSize = 16,
|
InReg = 16,
|
||||||
MustProgress = 17,
|
InlineHint = 17,
|
||||||
Naked = 18,
|
JumpTable = 18,
|
||||||
Nest = 19,
|
MinSize = 19,
|
||||||
NoAlias = 20,
|
MustProgress = 20,
|
||||||
NoBuiltin = 21,
|
Naked = 21,
|
||||||
NoCallback = 22,
|
Nest = 22,
|
||||||
NoCapture = 23,
|
NoAlias = 23,
|
||||||
NoCfCheck = 24,
|
NoBuiltin = 24,
|
||||||
NoDuplicate = 25,
|
NoCallback = 25,
|
||||||
NoFree = 26,
|
NoCfCheck = 26,
|
||||||
NoImplicitFloat = 27,
|
NoDivergenceSource = 27,
|
||||||
NoInline = 28,
|
NoDuplicate = 28,
|
||||||
NoMerge = 29,
|
NoExt = 29,
|
||||||
NoProfile = 30,
|
NoFree = 30,
|
||||||
NoRecurse = 31,
|
NoImplicitFloat = 31,
|
||||||
NoRedZone = 32,
|
NoInline = 32,
|
||||||
NoReturn = 33,
|
NoMerge = 33,
|
||||||
NoSanitizeBounds = 34,
|
NoProfile = 34,
|
||||||
NoSanitizeCoverage = 35,
|
NoRecurse = 35,
|
||||||
NoSync = 36,
|
NoRedZone = 36,
|
||||||
NoUndef = 37,
|
NoReturn = 37,
|
||||||
NoUnwind = 38,
|
NoSanitizeBounds = 38,
|
||||||
NonLazyBind = 39,
|
NoSanitizeCoverage = 39,
|
||||||
NonNull = 40,
|
NoSync = 40,
|
||||||
NullPointerIsValid = 41,
|
NoUndef = 41,
|
||||||
OptForFuzzing = 42,
|
NoUnwind = 42,
|
||||||
OptimizeForDebugging = 43,
|
NonLazyBind = 43,
|
||||||
OptimizeForSize = 44,
|
NonNull = 44,
|
||||||
OptimizeNone = 45,
|
NullPointerIsValid = 45,
|
||||||
PresplitCoroutine = 46,
|
OptForFuzzing = 46,
|
||||||
ReadNone = 47,
|
OptimizeForDebugging = 47,
|
||||||
ReadOnly = 48,
|
OptimizeForSize = 48,
|
||||||
Returned = 49,
|
OptimizeNone = 49,
|
||||||
ReturnsTwice = 50,
|
PresplitCoroutine = 50,
|
||||||
SExt = 51,
|
ReadNone = 51,
|
||||||
SafeStack = 52,
|
ReadOnly = 52,
|
||||||
SanitizeAddress = 53,
|
Returned = 53,
|
||||||
SanitizeHWAddress = 54,
|
ReturnsTwice = 54,
|
||||||
SanitizeMemTag = 55,
|
SExt = 55,
|
||||||
SanitizeMemory = 56,
|
SafeStack = 56,
|
||||||
SanitizeThread = 57,
|
SanitizeAddress = 57,
|
||||||
ShadowCallStack = 58,
|
SanitizeHWAddress = 58,
|
||||||
SkipProfile = 59,
|
SanitizeMemTag = 59,
|
||||||
Speculatable = 60,
|
SanitizeMemory = 60,
|
||||||
SpeculativeLoadHardening = 61,
|
SanitizeNumericalStability = 61,
|
||||||
StackProtect = 62,
|
SanitizeRealtime = 62,
|
||||||
StackProtectReq = 63,
|
SanitizeRealtimeBlocking = 63,
|
||||||
StackProtectStrong = 64,
|
SanitizeThread = 64,
|
||||||
StrictFP = 65,
|
SanitizeType = 65,
|
||||||
SwiftAsync = 66,
|
ShadowCallStack = 66,
|
||||||
SwiftError = 67,
|
SkipProfile = 67,
|
||||||
SwiftSelf = 68,
|
Speculatable = 68,
|
||||||
WillReturn = 69,
|
SpeculativeLoadHardening = 69,
|
||||||
Writable = 70,
|
StackProtect = 70,
|
||||||
WriteOnly = 71,
|
StackProtectReq = 71,
|
||||||
ZExt = 72,
|
StackProtectStrong = 72,
|
||||||
// LastEnumAttr = 72,
|
StrictFP = 73,
|
||||||
// FirstTypeAttr = 73,
|
SwiftAsync = 74,
|
||||||
ByRef = 73,
|
SwiftError = 75,
|
||||||
ByVal = 74,
|
SwiftSelf = 76,
|
||||||
ElementType = 75,
|
WillReturn = 77,
|
||||||
InAlloca = 76,
|
Writable = 78,
|
||||||
Preallocated = 77,
|
WriteOnly = 79,
|
||||||
StructRet = 78,
|
ZExt = 80,
|
||||||
// LastTypeAttr = 78,
|
//LastEnumAttr = 80,
|
||||||
// FirstIntAttr = 79,
|
//FirstTypeAttr = 81,
|
||||||
Alignment = 79,
|
ByRef = 81,
|
||||||
AllocKind = 80,
|
ByVal = 82,
|
||||||
AllocSize = 81,
|
ElementType = 83,
|
||||||
Dereferenceable = 82,
|
InAlloca = 84,
|
||||||
DereferenceableOrNull = 83,
|
Preallocated = 85,
|
||||||
Memory = 84,
|
StructRet = 86,
|
||||||
NoFPClass = 85,
|
//LastTypeAttr = 86,
|
||||||
StackAlignment = 86,
|
//FirstIntAttr = 87,
|
||||||
UWTable = 87,
|
Alignment = 87,
|
||||||
VScaleRange = 88,
|
AllocKind = 88,
|
||||||
// LastIntAttr = 88,
|
AllocSize = 89,
|
||||||
|
Captures = 90,
|
||||||
|
Dereferenceable = 91,
|
||||||
|
DereferenceableOrNull = 92,
|
||||||
|
Memory = 93,
|
||||||
|
NoFPClass = 94,
|
||||||
|
StackAlignment = 95,
|
||||||
|
UWTable = 96,
|
||||||
|
VScaleRange = 97,
|
||||||
|
//LastIntAttr = 97,
|
||||||
|
//FirstConstantRangeAttr = 98,
|
||||||
|
Range = 98,
|
||||||
|
//LastConstantRangeAttr = 98,
|
||||||
|
//FirstConstantRangeListAttr = 99,
|
||||||
|
Initializes = 99,
|
||||||
|
//LastConstantRangeListAttr = 99,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for Attribute {
|
impl TryFrom<&str> for Attribute {
|
||||||
|
|||||||
@@ -838,8 +838,7 @@ impl<'ctx> Context<'ctx> {
|
|||||||
.builder()
|
.builder()
|
||||||
.build_call(intrinsic, &[value.into()], "call_byte_swap")?
|
.build_call(intrinsic, &[value.into()], "call_byte_swap")?
|
||||||
.try_as_basic_value()
|
.try_as_basic_value()
|
||||||
.left()
|
.unwrap_basic())
|
||||||
.unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a GEP instruction.
|
/// Builds a GEP instruction.
|
||||||
@@ -912,7 +911,7 @@ impl<'ctx> Context<'ctx> {
|
|||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.try_as_basic_value()
|
.try_as_basic_value()
|
||||||
.left()
|
.basic()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a call to the runtime API `import`, where `import` is a "getter" API.
|
/// Builds a call to the runtime API `import`, where `import` is a "getter" API.
|
||||||
@@ -950,7 +949,7 @@ impl<'ctx> Context<'ctx> {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.modify_call_site_value(arguments, call_site_value, function);
|
self.modify_call_site_value(arguments, call_site_value, function);
|
||||||
call_site_value.try_as_basic_value().left()
|
call_site_value.try_as_basic_value().basic()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the alignment to `1`, since all non-stack memory pages have such alignment.
|
/// Sets the alignment to `1`, since all non-stack memory pages have such alignment.
|
||||||
@@ -1082,13 +1081,7 @@ impl<'ctx> Context<'ctx> {
|
|||||||
|
|
||||||
Ok(call_site_value
|
Ok(call_site_value
|
||||||
.try_as_basic_value()
|
.try_as_basic_value()
|
||||||
.left()
|
.unwrap_basic()
|
||||||
.unwrap_or_else(|| {
|
|
||||||
panic!(
|
|
||||||
"revive runtime function {} should return a value",
|
|
||||||
<PolkaVMSbrkFunction as RuntimeFunction>::NAME,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.into_pointer_value())
|
.into_pointer_value())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1277,7 +1270,7 @@ impl<'ctx> Context<'ctx> {
|
|||||||
call_site_value.add_attribute(
|
call_site_value.add_attribute(
|
||||||
inkwell::attributes::AttributeLoc::Param(index as u32),
|
inkwell::attributes::AttributeLoc::Param(index as u32),
|
||||||
self.llvm
|
self.llvm
|
||||||
.create_enum_attribute(Attribute::NoCapture as u32, 0),
|
.create_enum_attribute(Attribute::Captures as u32, 0), // captures(none)
|
||||||
);
|
);
|
||||||
call_site_value.add_attribute(
|
call_site_value.add_attribute(
|
||||||
inkwell::attributes::AttributeLoc::Param(index as u32),
|
inkwell::attributes::AttributeLoc::Param(index as u32),
|
||||||
|
|||||||
@@ -275,6 +275,6 @@ pub fn count_leading_zeros<'ctx>(
|
|||||||
"clz",
|
"clz",
|
||||||
)?
|
)?
|
||||||
.try_as_basic_value()
|
.try_as_basic_value()
|
||||||
.left()
|
.basic()
|
||||||
.expect("the llvm.ctlz should return a value"))
|
.expect("the llvm.ctlz should return a value"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ use inkwell::values::BasicValue;
|
|||||||
|
|
||||||
use crate::polkavm::context::Context;
|
use crate::polkavm::context::Context;
|
||||||
|
|
||||||
const STATIC_CALL_FLAG: u64 = 0b0001_0000;
|
const STATIC_CALL_FLAG: u32 = 0b0001_0000;
|
||||||
const REENTRANT_CALL_FLAG: u64 = 0b0000_1000;
|
const REENTRANT_CALL_FLAG: u32 = 0b0000_1000;
|
||||||
|
const SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD: u64 = 2300;
|
||||||
|
|
||||||
/// Translates a contract call.
|
/// Translates a contract call.
|
||||||
pub fn call<'ctx>(
|
pub fn call<'ctx>(
|
||||||
@@ -37,12 +38,33 @@ pub fn call<'ctx>(
|
|||||||
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
|
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
|
||||||
context.build_store(output_length_pointer, output_length)?;
|
context.build_store(output_length_pointer, output_length)?;
|
||||||
|
|
||||||
let flags = if static_call {
|
let (flags, deposit_limit_value) = if static_call {
|
||||||
REENTRANT_CALL_FLAG | STATIC_CALL_FLAG
|
let flags = REENTRANT_CALL_FLAG | STATIC_CALL_FLAG;
|
||||||
|
(
|
||||||
|
context.xlen_type().const_int(flags as u64, false),
|
||||||
|
context.word_type().const_zero(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
REENTRANT_CALL_FLAG
|
call_reentrancy_heuristic(context, gas, input_length, output_length)?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let deposit_pointer = context.build_alloca_at_entry(context.word_type(), "deposit_pointer");
|
||||||
|
context.build_store(deposit_pointer, deposit_limit_value)?;
|
||||||
|
|
||||||
|
let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||||
|
context.builder(),
|
||||||
|
context.llvm(),
|
||||||
|
flags,
|
||||||
|
address_pointer.to_int(context),
|
||||||
|
"address_and_callee",
|
||||||
|
)?;
|
||||||
|
let deposit_and_value = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||||
|
context.builder(),
|
||||||
|
context.llvm(),
|
||||||
|
deposit_pointer.to_int(context),
|
||||||
|
value_pointer.to_int(context),
|
||||||
|
"deposit_and_value",
|
||||||
|
)?;
|
||||||
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||||
context.builder(),
|
context.builder(),
|
||||||
context.llvm(),
|
context.llvm(),
|
||||||
@@ -63,10 +85,10 @@ pub fn call<'ctx>(
|
|||||||
.build_runtime_call(
|
.build_runtime_call(
|
||||||
name,
|
name,
|
||||||
&[
|
&[
|
||||||
context.xlen_type().const_int(flags, false).into(),
|
flags_and_callee.into(),
|
||||||
address_pointer.to_int(context).into(),
|
context.register_type().const_all_ones().into(),
|
||||||
value_pointer.to_int(context).into(),
|
context.register_type().const_all_ones().into(),
|
||||||
clip_call_gas(context, gas)?,
|
deposit_and_value.into(),
|
||||||
input_data.into(),
|
input_data.into(),
|
||||||
output_data.into(),
|
output_data.into(),
|
||||||
],
|
],
|
||||||
@@ -89,7 +111,7 @@ pub fn call<'ctx>(
|
|||||||
|
|
||||||
pub fn delegate_call<'ctx>(
|
pub fn delegate_call<'ctx>(
|
||||||
context: &mut Context<'ctx>,
|
context: &mut Context<'ctx>,
|
||||||
gas: inkwell::values::IntValue<'ctx>,
|
_gas: inkwell::values::IntValue<'ctx>,
|
||||||
address: inkwell::values::IntValue<'ctx>,
|
address: inkwell::values::IntValue<'ctx>,
|
||||||
input_offset: inkwell::values::IntValue<'ctx>,
|
input_offset: inkwell::values::IntValue<'ctx>,
|
||||||
input_length: inkwell::values::IntValue<'ctx>,
|
input_length: inkwell::values::IntValue<'ctx>,
|
||||||
@@ -110,6 +132,18 @@ pub fn delegate_call<'ctx>(
|
|||||||
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
|
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
|
||||||
context.build_store(output_length_pointer, output_length)?;
|
context.build_store(output_length_pointer, output_length)?;
|
||||||
|
|
||||||
|
let deposit_pointer = context.build_alloca_at_entry(context.word_type(), "deposit_pointer");
|
||||||
|
context.build_store(deposit_pointer, context.word_type().const_all_ones())?;
|
||||||
|
|
||||||
|
let flags = context.xlen_type().const_int(0u64, false);
|
||||||
|
|
||||||
|
let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||||
|
context.builder(),
|
||||||
|
context.llvm(),
|
||||||
|
flags,
|
||||||
|
address_pointer.to_int(context),
|
||||||
|
"address_and_callee",
|
||||||
|
)?;
|
||||||
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||||
context.builder(),
|
context.builder(),
|
||||||
context.llvm(),
|
context.llvm(),
|
||||||
@@ -130,9 +164,10 @@ pub fn delegate_call<'ctx>(
|
|||||||
.build_runtime_call(
|
.build_runtime_call(
|
||||||
name,
|
name,
|
||||||
&[
|
&[
|
||||||
context.xlen_type().const_int(0u64, false).into(),
|
flags_and_callee.into(),
|
||||||
address_pointer.to_int(context).into(),
|
context.register_type().const_all_ones().into(),
|
||||||
clip_call_gas(context, gas)?,
|
context.register_type().const_all_ones().into(),
|
||||||
|
deposit_pointer.to_int(context).into(),
|
||||||
input_data.into(),
|
input_data.into(),
|
||||||
output_data.into(),
|
output_data.into(),
|
||||||
],
|
],
|
||||||
@@ -166,22 +201,82 @@ pub fn linker_symbol<'ctx>(
|
|||||||
context.build_load_address(context.get_global(path)?.into())
|
context.build_load_address(context.get_global(path)?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The runtime implements gas as `u64` so we clip the stipend to `u64::MAX`.
|
/// The Solidity `address.transfer` and `address.send` call detection heuristic.
|
||||||
fn clip_call_gas<'ctx>(
|
///
|
||||||
context: &Context<'ctx>,
|
/// # Why
|
||||||
|
/// This heuristic is an additional security feature to guard against re-entrancy attacks
|
||||||
|
/// in case contract authors violate Solidity best practices and use `address.transfer` or
|
||||||
|
/// `address.send`.
|
||||||
|
/// While contract authors are supposed to never use `address.transfer` or `address.send`,
|
||||||
|
/// for a small cost we can be extra defensive about it.
|
||||||
|
///
|
||||||
|
/// # How
|
||||||
|
/// The gas stipend emitted by solc for `transfer` and `send` is not static, thus:
|
||||||
|
/// - Dynamically allow re-entrancy only for calls considered not transfer or send.
|
||||||
|
/// - Detected balance transfers will supply 0 deposit limit instead of `u256::MAX`.
|
||||||
|
///
|
||||||
|
/// Calls are considered transfer or send if:
|
||||||
|
/// - (Input length | Output lenght) == 0;
|
||||||
|
/// - Gas <= 2300;
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// The call flags xlen `IntValue` and the deposit limit word `IntValue`.
|
||||||
|
fn call_reentrancy_heuristic<'ctx>(
|
||||||
|
context: &mut Context<'ctx>,
|
||||||
gas: inkwell::values::IntValue<'ctx>,
|
gas: inkwell::values::IntValue<'ctx>,
|
||||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
|
input_length: inkwell::values::IntValue<'ctx>,
|
||||||
let builder = context.builder();
|
output_length: inkwell::values::IntValue<'ctx>,
|
||||||
|
) -> anyhow::Result<(
|
||||||
let clipped = context.register_type().const_all_ones();
|
inkwell::values::IntValue<'ctx>,
|
||||||
let is_overflow = builder.build_int_compare(
|
inkwell::values::IntValue<'ctx>,
|
||||||
inkwell::IntPredicate::UGT,
|
)> {
|
||||||
gas,
|
// Branch-free SSA implementation: First derive the heuristic boolean (int1) value.
|
||||||
builder.build_int_z_extend(clipped, context.word_type(), "gas_clipped")?,
|
let input_length_or_output_length =
|
||||||
"is_gas_overflow",
|
context
|
||||||
|
.builder()
|
||||||
|
.build_or(input_length, output_length, "input_length_or_output_length")?;
|
||||||
|
let is_no_input_no_output = context.builder().build_int_compare(
|
||||||
|
inkwell::IntPredicate::EQ,
|
||||||
|
context.xlen_type().const_zero(),
|
||||||
|
input_length_or_output_length,
|
||||||
|
"is_no_input_no_output",
|
||||||
)?;
|
)?;
|
||||||
let truncated = builder.build_int_truncate(gas, context.register_type(), "gas_truncated")?;
|
let gas_stipend = context
|
||||||
let call_gas = builder.build_select(is_overflow, clipped, truncated, "call_gas")?;
|
.word_type()
|
||||||
|
.const_int(SOLIDITY_TRANSFER_GAS_STIPEND_THRESHOLD, false);
|
||||||
|
let is_gas_stipend_for_transfer_or_send = context.builder().build_int_compare(
|
||||||
|
inkwell::IntPredicate::ULE,
|
||||||
|
gas,
|
||||||
|
gas_stipend,
|
||||||
|
"is_gas_stipend_for_transfer_or_send",
|
||||||
|
)?;
|
||||||
|
let is_balance_transfer = context.builder().build_and(
|
||||||
|
is_no_input_no_output,
|
||||||
|
is_gas_stipend_for_transfer_or_send,
|
||||||
|
"is_balance_transfer",
|
||||||
|
)?;
|
||||||
|
let is_regular_call = context
|
||||||
|
.builder()
|
||||||
|
.build_not(is_balance_transfer, "is_balance_transfer_inverted")?;
|
||||||
|
|
||||||
Ok(call_gas)
|
// Call flag: Left shift the heuristic boolean value.
|
||||||
|
let is_regular_call_xlen = context.builder().build_int_z_extend(
|
||||||
|
is_regular_call,
|
||||||
|
context.xlen_type(),
|
||||||
|
"is_balance_transfer_xlen",
|
||||||
|
)?;
|
||||||
|
let call_flags = context.builder().build_left_shift(
|
||||||
|
is_regular_call_xlen,
|
||||||
|
context.xlen_type().const_int(3, false),
|
||||||
|
"flags",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Deposit limit value: Sign-extended the heuristic boolean value.
|
||||||
|
let deposit_limit_value = context.builder().build_int_s_extend(
|
||||||
|
is_regular_call,
|
||||||
|
context.word_type(),
|
||||||
|
"deposit_limit_value",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok((call_flags, deposit_limit_value))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ mimalloc = { version = "0.1.46", default-features = false }
|
|||||||
|
|
||||||
[target.'cfg(target_os = "emscripten")'.dependencies]
|
[target.'cfg(target_os = "emscripten")'.dependencies]
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
inkwell = { workspace = true, features = ["target-riscv", "llvm18-1-no-llvm-linking"]}
|
inkwell = { workspace = true, features = ["target-riscv", "llvm21-1-no-llvm-linking"]}
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
git2 = { workspace = true, default-features = false }
|
git2 = { workspace = true, default-features = false }
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ description = "Implements the low level runtime API bindings with pallet contrac
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
inkwell = { workspace = true, features = ["target-riscv", "no-libffi-linking", "llvm18-1"] }
|
inkwell = { workspace = true, features = ["target-riscv", "no-libffi-linking", "llvm21-1"] }
|
||||||
|
|
||||||
revive-common = { workspace = true }
|
revive-common = { workspace = true }
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ POLKAVM_IMPORT(void, block_hash, uint32_t, uint32_t)
|
|||||||
|
|
||||||
POLKAVM_IMPORT(void, block_number, uint32_t)
|
POLKAVM_IMPORT(void, block_number, uint32_t)
|
||||||
|
|
||||||
POLKAVM_IMPORT(uint32_t, call_evm, uint32_t, uint32_t, uint32_t, uint64_t, uint64_t, uint64_t)
|
POLKAVM_IMPORT(uint32_t, call, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t)
|
||||||
|
|
||||||
POLKAVM_IMPORT(void, call_data_copy, uint32_t, uint32_t, uint32_t)
|
POLKAVM_IMPORT(void, call_data_copy, uint32_t, uint32_t, uint32_t)
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ POLKAVM_IMPORT(void, code_hash, uint32_t, uint32_t)
|
|||||||
|
|
||||||
POLKAVM_IMPORT(void, consume_all_gas)
|
POLKAVM_IMPORT(void, consume_all_gas)
|
||||||
|
|
||||||
POLKAVM_IMPORT(uint32_t, delegate_call_evm, uint32_t, uint32_t, uint64_t, uint64_t, uint64_t)
|
POLKAVM_IMPORT(uint32_t, delegate_call, uint64_t, uint64_t, uint64_t, uint32_t, uint64_t, uint64_t)
|
||||||
|
|
||||||
POLKAVM_IMPORT(void, deposit_event, uint32_t, uint32_t, uint32_t, uint32_t)
|
POLKAVM_IMPORT(void, deposit_event, uint32_t, uint32_t, uint32_t, uint32_t)
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ pub static BLOCK_HASH: &str = "block_hash";
|
|||||||
|
|
||||||
pub static BLOCK_NUMBER: &str = "block_number";
|
pub static BLOCK_NUMBER: &str = "block_number";
|
||||||
|
|
||||||
pub static CALL: &str = "call_evm";
|
pub static CALL: &str = "call";
|
||||||
|
|
||||||
pub static CALL_DATA_COPY: &str = "call_data_copy";
|
pub static CALL_DATA_COPY: &str = "call_data_copy";
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ pub static CODE_SIZE: &str = "code_size";
|
|||||||
|
|
||||||
pub static CODE_HASH: &str = "code_hash";
|
pub static CODE_HASH: &str = "code_hash";
|
||||||
|
|
||||||
pub static DELEGATE_CALL: &str = "delegate_call_evm";
|
pub static DELEGATE_CALL: &str = "delegate_call";
|
||||||
|
|
||||||
pub static DEPOSIT_EVENT: &str = "deposit_event";
|
pub static DEPOSIT_EVENT: &str = "deposit_event";
|
||||||
|
|
||||||
|
|||||||
@@ -113,9 +113,10 @@ impl Error {
|
|||||||
let message = r#"
|
let message = r#"
|
||||||
Warning: It looks like you are using '<address payable>.send/transfer(<X>)'.
|
Warning: It looks like you are using '<address payable>.send/transfer(<X>)'.
|
||||||
Using '<address payable>.send/transfer(<X>)' is deprecated and strongly discouraged!
|
Using '<address payable>.send/transfer(<X>)' is deprecated and strongly discouraged!
|
||||||
The revive runtime uses a heuristic to detect '<address payable>.send/transfer(<X>)' calls and
|
The resolc compiler uses a heuristic to detect '<address payable>.send/transfer(<X>)' calls,
|
||||||
the gas stipend used by the runtime is different from the EVM.
|
which disables call re-entrancy and supplies all remaining gas instead of the 2300 gas stipend.
|
||||||
You are advised to carefully test this, employ re-entrancy guards or use the withdrawal pattern instead!
|
However, detection is not guaranteed. You are advised to carefully test this, employ
|
||||||
|
re-entrancy guards or use the withdrawal pattern instead!
|
||||||
Learn more on https://docs.soliditylang.org/en/latest/security-considerations.html#reentrancy
|
Learn more on https://docs.soliditylang.org/en/latest/security-considerations.html#reentrancy
|
||||||
and https://docs.soliditylang.org/en/latest/common-patterns.html#withdrawal-from-contracts
|
and https://docs.soliditylang.org/en/latest/common-patterns.html#withdrawal-from-contracts
|
||||||
"#
|
"#
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ repository.workspace = true
|
|||||||
description = "revive compiler stdlib components"
|
description = "revive compiler stdlib components"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
inkwell = { workspace = true, features = ["target-riscv", "no-libffi-linking", "llvm18-1"] }
|
inkwell = { workspace = true, features = ["target-riscv", "no-libffi-linking", "llvm21-1"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
revive-build-utils = { workspace = true }
|
revive-build-utils = { workspace = true }
|
||||||
|
|||||||
+1
-1
Submodule llvm updated: 3b5b5c1ec4...f68f64eb81
Reference in New Issue
Block a user