Compare commits

..

1 Commits

Author SHA1 Message Date
Cyrill Leutwiler c04c2560ac wip
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-09-06 09:55:43 +02:00
47 changed files with 1220 additions and 1549 deletions
-38
View File
@@ -1,38 +0,0 @@
name: Build revive-debian
on:
workflow_dispatch:
env:
REVIVE_DEBIAN_PACKAGE: revive-debian-x86
DEBIAN_CONTAINER: revive-builder-debian-x86
DEBIAN_CONTAINER_BUILDER: build-debian-builder.sh
DEBIAN_CONTAINER_RUNNER: run-debian-builder.sh
REVIVE_DEBIAN_INSTALL: ${{ github.workspace }}/target/release
REVIVE_DEBIAN_BINARY: resolc
RUST_VERSION: "1.80"
jobs:
build-revive-debian-x86:
name: debian-container-x86
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: build-container
run: |
(cd utils && ./${{ env.DEBIAN_CONTAINER_BUILDER}} --build-arg RUST_VERSION=${{ env.RUST_VERSION}} . )
- name: build-revive-debian
run: |
rustup show
cargo --version
rustup +nightly show
cargo +nightly --version
bash --version
utils/${{ env.DEBIAN_CONTAINER_RUNNER }} utils/build-revive.sh -o ${{ env.REVIVE_DEBIAN_INSTALL}}
- uses: actions/upload-artifact@v4
with:
name: ${{ env.REVIVE_DEBIAN_PACKAGE }}
path: ${{ env.REVIVE_DEBIAN_INSTALL }}/${{ env.REVIVE_DEBIAN_BINARY }}
retention-days: 1
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
- name: Install solc - name: Install solc
run: | run: |
mkdir -p solc mkdir -p solc
curl -sSL --output solc/solc https://github.com/ethereum/solidity/releases/download/v0.8.27/solc-static-linux curl -sSL --output solc/solc https://github.com/ethereum/solidity/releases/download/v0.8.26/solc-static-linux
chmod +x solc/solc chmod +x solc/solc
echo "$(pwd)/solc/" >> $GITHUB_PATH echo "$(pwd)/solc/" >> $GITHUB_PATH
Generated
+673 -784
View File
File diff suppressed because it is too large Load Diff
+2 -3
View File
@@ -10,8 +10,7 @@ authors = [
] ]
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
edition = "2021" edition = "2021"
repository = "https://github.com/paritytech/revive" repository = "https://github.com/xermicus/revive"
rust-version = "1.80.0"
[workspace.dependencies] [workspace.dependencies]
revive-benchmarks = { version = "0.1.0", path = "crates/benchmarks" } revive-benchmarks = { version = "0.1.0", path = "crates/benchmarks" }
@@ -67,7 +66,7 @@ log = { version = "0.4" }
# polkadot-sdk and friends # polkadot-sdk and friends
codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" } codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" }
scale-info = { version = "2.11.1", default-features = false } scale-info = { version = "2.11.1", default-features = false }
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "c77095f51119d2eccdc54d2f3518bed0ffbd6d53" } polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "d7b575338b2c647e04fc48bfbe00ea8f492fb580" }
# llvm # llvm
[workspace.dependencies.inkwell] [workspace.dependencies.inkwell]
-7
View File
@@ -8,13 +8,6 @@ install-bin:
install-npm: install-npm:
npm install && npm fund npm install && npm fund
# install-revive: Build and install to the directory specified in REVIVE_INSTALL_DIR
ifeq ($(origin REVIVE_INSTALL_DIR), undefined)
REVIVE_INSTALL_DIR=`pwd`/release/revive-debian
endif
install-revive:
cargo install --path crates/solidity --root $(REVIVE_INSTALL_DIR)
format: format:
cargo fmt --all --check cargo fmt --all --check
+1 -1
View File
@@ -1,4 +1,4 @@
![CI](https://github.com/paritytech/revive/actions/workflows/rust.yml/badge.svg) ![CI](https://github.com/xermicus/revive/actions/workflows/rust.yml/badge.svg)
# revive # revive
+1 -1
View File
@@ -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 = BIT_LENGTH_WORD; pub const BIT_LENGTH_VALUE: usize = crate::byte_length::BYTE_LENGTH_VALUE * BIT_LENGTH_BYTE;
/// 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 -3
View File
@@ -6,12 +6,15 @@ 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;
+2 -12
View File
@@ -1,6 +1,6 @@
{ {
"config": { "config": {
"chainId": 420420420, "chainId": 1,
"homesteadBlock": 0, "homesteadBlock": 0,
"eip150Block": 0, "eip150Block": 0,
"eip155Block": 0, "eip155Block": 0,
@@ -26,15 +26,5 @@
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00", "timestamp": "0x00",
"alloc": { "alloc": {}
"0101010101010101010101010101010101010101": {
"balance": "1000000000"
},
"0202020202020202020202020202020202020202": {
"balance": "1000000000"
},
"0303030303030303030303030303030303030303": {
"balance": "1000000000"
}
}
} }
+3 -2
View File
@@ -100,9 +100,10 @@ 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. /// Panics if the gas used is zero as this indicates nothing was run, i.e.
/// 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: {self:?}"); assert_ne!(self.gas_used, U256::ZERO, "nothing was executed");
self.error.is_none() self.error.is_none()
} }
} }
+8 -8
View File
@@ -1,10 +1,10 @@
{ {
"Baseline": 912, "Baseline": 878,
"Computation": 4413, "Computation": 4305,
"DivisionArithmetics": 40689, "DivisionArithmetics": 39774,
"ERC20": 54374, "ERC20": 53405,
"Events": 1726, "Events": 1693,
"FibonacciIterative": 3015, "FibonacciIterative": 2917,
"Flipper": 3612, "Flipper": 3570,
"SHA1": 32865 "SHA1": 32557
} }
+15 -24
View File
@@ -7,10 +7,10 @@ pragma solidity ^0.8;
"differential": true, "differential": true,
"actions": [ "actions": [
{ {
"Upload": { "Instantiate": {
"code": { "code": {
"Solidity": { "Solidity": {
"contract": "Callee" "contract": "Call"
} }
} }
} }
@@ -19,7 +19,7 @@ pragma solidity ^0.8;
"Instantiate": { "Instantiate": {
"code": { "code": {
"Solidity": { "Solidity": {
"contract": "Caller" "contract": "Call"
} }
} }
} }
@@ -27,37 +27,28 @@ pragma solidity ^0.8;
{ {
"Call": { "Call": {
"dest": { "dest": {
"Instantiated": 0 "Instantiated": 1
}, },
"value": 123, "data": "1b8b921d0000000000000000000000001c81a61a407017c58397a47d2ab28191b9b8ec9b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000050102030405000000000000000000000000000000000000000000000000000000"
"data": "1eb16e5b000000000000000000000000d8b934580fce35a11b58c6d73adee468a2833fa8"
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "5a6535fc00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004cafebabe00000000000000000000000000000000000000000000000000000000"
} }
} }
] ]
} }
*/ */
contract Callee { contract Call {
function echo(bytes memory payload) public pure returns (bytes memory) {
return payload;
}
}
contract Caller {
function value_transfer(address payable destination) public payable { function value_transfer(address payable destination) public payable {
destination.transfer(msg.value); destination.transfer(msg.value);
} }
function call(bytes memory payload) public returns (bytes memory) { function echo(bytes memory payload) public pure returns (bytes memory) {
Callee callee = new Callee(); return payload;
return callee.echo(payload); }
function call(
address callee,
bytes memory payload
) public pure returns (bytes memory) {
return Call(callee).echo(payload);
} }
} }
-16
View File
@@ -30,14 +30,6 @@ pragma solidity ^0.8;
}, },
"data": "fc9c8d39" "data": "fc9c8d39"
} }
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "3af973b1"
}
} }
] ]
} }
@@ -51,12 +43,4 @@ 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;
}
} }
+6 -57
View File
@@ -1,64 +1,13 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8; pragma solidity ^0.8.24;
/* runner.json
{
"differential": true,
"actions": [
{
"Upload": {
"code": {
"Solidity": {
"contract": "CreateA"
}
}
}
},
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "CreateB"
}
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"value": 10000
}
},
{
"Call": {
"dest": {
"Instantiated": 0
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
}
}
}
]
}
*/
contract CreateA { contract CreateA {
constructor() payable {} address creator;
constructor() payable {
creator = msg.sender;
}
} }
contract CreateB { contract CreateB {
+11 -13
View File
@@ -4,32 +4,30 @@ pragma solidity ^0.8.24;
/* runner.json /* runner.json
{ {
"differential": true, "actions": [
"actions": [
{ {
"Instantiate": { "Instantiate": {}
"code": {
"Solidity": {
"contract": "TestSha3"
}
}
}
}, },
{ {
"Call": { "Call": {
"dest": { "dest": {
"Instantiated": 0 "Instantiated": 0
}, },
"data": "f9fbd5540000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c68656c6c6f20776f726c64210000000000000000000000000000000000000000" "data": "f9fbd5540000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c68656c6c6f20776f726c64210000000000000000000000000000000000000000"
} }
},
{
"VerifyCall": {
"success": true,
"output": "57caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd6"
}
} }
] ]
} }
*/ */
contract TestSha3 { contract TestSha3 {
function test(string memory _pre) external payable returns (bytes32) { function test(string memory _pre) external payable returns (bytes32 hash) {
bytes32 hash = keccak256(bytes(_pre)); hash = keccak256(bytes(_pre));
return bytes32(uint(hash) + 1);
} }
} }
+7 -3
View File
@@ -22,7 +22,12 @@ pragma solidity ^0.8;
}, },
"data": "4d43bec90000000000000000000000000000000000000000000000000000000000000000" "data": "4d43bec90000000000000000000000000000000000000000000000000000000000000000"
} }
}, }
]
}
*/
/* TODO when pallet_revive accepts Solidity event topics
{ {
"Call": { "Call": {
"dest": { "dest": {
@@ -31,8 +36,7 @@ pragma solidity ^0.8;
"data": "4d43bec9000000000000000000000000000000000000000000000000000000000000007b" "data": "4d43bec9000000000000000000000000000000000000000000000000000000000000007b"
} }
} }
]
}
*/ */
contract Events { contract Events {
@@ -1,53 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Upload": {
"code": {
"Solidity": {
"contract": "Callee"
}
}
}
},
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "ReturnDataOob"
}
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
}
}
}
]
}
*/
contract Callee {
function echo(bytes memory payload) public pure returns (bytes memory) {
return payload;
}
}
contract ReturnDataOob {
fallback() external {
new Callee().echo(hex"1234");
assembly {
let pos := mload(64)
let size := add(returndatasize(), 1)
returndatacopy(pos, 0, size)
}
}
}
-54
View File
@@ -1,54 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "Transfer"
}
},
"value": 11
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "1c8d16b30000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000a"
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "fb9e8d0500000000000000000000000003030303030303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000001"
}
}
]
}
*/
contract Transfer {
constructor() payable {
transfer_self(msg.value);
}
function address_self() internal view returns (address payable) {
return payable(address(this));
}
function transfer_self(uint _amount) public payable {
transfer_to(address_self(), _amount);
}
function transfer_to(address payable _dest, uint _amount) public payable {
_dest.transfer(_amount);
}
}
-16
View File
@@ -7,7 +7,6 @@ pragma solidity ^0.8;
"actions": [ "actions": [
{ {
"Instantiate": { "Instantiate": {
"value": 1024,
"code": { "code": {
"Solidity": { "Solidity": {
"contract": "Value" "contract": "Value"
@@ -20,33 +19,18 @@ pragma solidity ^0.8;
"dest": { "dest": {
"Instantiated": 0 "Instantiated": 0
}, },
"value": 123,
"data": "3fa4f245" "data": "3fa4f245"
} }
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "52da5fa0"
}
} }
] ]
} }
*/ */
contract Value { contract Value {
constructor() payable {}
function value() public payable returns (uint ret) { function value() public payable returns (uint ret) {
ret = msg.value; ret = msg.value;
} }
function balance_self() public view returns (uint ret) {
ret = address(this).balance;
}
function balance_of(address _address) public view returns (uint ret) { function balance_of(address _address) public view returns (uint ret) {
ret = _address.balance; ret = _address.balance;
} }
-2
View File
@@ -209,11 +209,9 @@ case!("Call.sol", "Call", vec![], call_constructor);
sol!( sol!(
contract Value { contract Value {
function balance_of(address _address) public view returns (uint ret); function balance_of(address _address) public view returns (uint ret);
function balance_self() public view returns (uint ret);
} }
); );
case!("Value.sol", Value, balance_ofCall, value_balance_of, address: Address); case!("Value.sol", Value, balance_ofCall, value_balance_of, address: Address);
case!("Value.sol", Value, balance_selfCall, value_balance_self,);
sol!( sol!(
contract Bitwise { contract Bitwise {
+112 -35
View File
@@ -6,10 +6,6 @@ use SpecsAction::*;
use crate::cases::Contract; use crate::cases::Contract;
/// Parameters:
/// - The function name of the test
/// - The contract name to fill in empty code based on the file path
/// - The contract source file
macro_rules! test_spec { macro_rules! test_spec {
($test_name:ident, $contract_name:literal, $source_file:literal) => { ($test_name:ident, $contract_name:literal, $source_file:literal) => {
#[test] #[test]
@@ -30,6 +26,7 @@ test_spec!(hash_keccak_256, "TestSha3", "Crypto.sol");
test_spec!(erc20, "ERC20", "ERC20.sol"); test_spec!(erc20, "ERC20", "ERC20.sol");
test_spec!(computation, "Computation", "Computation.sol"); test_spec!(computation, "Computation", "Computation.sol");
test_spec!(msize, "MSize", "MSize.sol"); test_spec!(msize, "MSize", "MSize.sol");
test_spec!(transferred_value, "Value", "Value.sol");
test_spec!(sha1, "SHA1", "SHA1.sol"); test_spec!(sha1, "SHA1", "SHA1.sol");
test_spec!(block, "Block", "Block.sol"); test_spec!(block, "Block", "Block.sol");
test_spec!(mcopy, "MCopy", "MCopy.sol"); test_spec!(mcopy, "MCopy", "MCopy.sol");
@@ -37,11 +34,6 @@ test_spec!(events, "Events", "Events.sol");
test_spec!(storage, "Storage", "Storage.sol"); test_spec!(storage, "Storage", "Storage.sol");
test_spec!(mstore8, "MStore8", "MStore8.sol"); test_spec!(mstore8, "MStore8", "MStore8.sol");
test_spec!(address, "Context", "Context.sol"); test_spec!(address, "Context", "Context.sol");
test_spec!(balance, "Value", "Value.sol");
test_spec!(create, "CreateB", "Create.sol");
test_spec!(call, "Caller", "Call.sol");
test_spec!(transfer, "Transfer", "Transfer.sol");
test_spec!(return_data_oob, "ReturnDataOob", "ReturnDataOob.sol");
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> { fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
vec![Instantiate { vec![Instantiate {
@@ -232,35 +224,12 @@ fn signed_remainder() {
} }
/* /*
// These test were implement for the mock-runtime and need to be ported yet.
#[test] #[test]
fn create2_failure() { fn events() {
let mut state = State::default(); assert_success(&Contract::event(U256::ZERO), true);
let contract_a = Contract::create_a(); assert_success(&Contract::event(U256::from(123)), true);
state.upload_code(&contract_a.pvm_runtime);
let contract = Contract::create_b();
let (state, output) = state
.transaction()
.with_default_account(&contract.pvm_runtime)
.calldata(contract.calldata.clone())
.call();
assert_eq!(output.flags, ReturnFlags::Success);
// The address already exists, which should cause the contract to revert
let (_, output) = state
.transaction()
.with_default_account(&contract.pvm_runtime)
.calldata(contract.calldata)
.call();
assert_eq!(output.flags, ReturnFlags::Revert);
} }
#[test] #[test]
fn balance() { fn balance() {
let (_, output) = assert_success(&Contract::value_balance_of(Default::default()), false); let (_, output) = assert_success(&Contract::value_balance_of(Default::default()), false);
@@ -286,6 +255,84 @@ fn balance() {
assert_eq!(expected, received) assert_eq!(expected, received)
} }
#[test]
fn create2() {
let mut state = State::default();
let contract_a = Contract::create_a();
state.upload_code(&contract_a.pvm_runtime);
let contract = Contract::create_b();
let (state, output) = state
.transaction()
.with_default_account(&contract.pvm_runtime)
.calldata(contract.calldata)
.call();
assert_eq!(output.flags, ReturnFlags::Success);
assert_eq!(state.accounts().len(), 2);
for address in state.accounts().keys() {
if *address != Transaction::default_address() {
let derived_address = Transaction::default_address().create2(
B256::from(U256::from(1)),
keccak256(&contract_a.pvm_runtime).0,
);
assert_eq!(*address, derived_address);
}
}
}
#[test]
fn create2_failure() {
let mut state = State::default();
let contract_a = Contract::create_a();
state.upload_code(&contract_a.pvm_runtime);
let contract = Contract::create_b();
let (state, output) = state
.transaction()
.with_default_account(&contract.pvm_runtime)
.calldata(contract.calldata.clone())
.call();
assert_eq!(output.flags, ReturnFlags::Success);
// The address already exists, which should cause the contract to revert
let (_, output) = state
.transaction()
.with_default_account(&contract.pvm_runtime)
.calldata(contract.calldata)
.call();
assert_eq!(output.flags, ReturnFlags::Revert);
}
#[test]
fn create_with_value() {
let mut state = State::default();
state.upload_code(&Contract::create_a().pvm_runtime);
let amount = U256::from(123);
let contract = Contract::create_b();
let (state, output) = state
.transaction()
.with_default_account(&contract.pvm_runtime)
.callvalue(amount)
.call();
assert_eq!(output.flags, ReturnFlags::Success);
assert_eq!(state.accounts().len(), 2);
for (address, account) in state.accounts() {
if *address == Transaction::default_address() {
assert_eq!(account.value, U256::ZERO);
} else {
assert_eq!(account.value, amount);
}
}
}
#[test] #[test]
fn ext_code_size() { fn ext_code_size() {
let contract = Contract::ext_code_size(Transaction::default_address()); let contract = Contract::ext_code_size(Transaction::default_address());
@@ -309,4 +356,34 @@ fn code_size() {
let received = U256::from_be_slice(&output.data); let received = U256::from_be_slice(&output.data);
assert_eq!(expected, received); assert_eq!(expected, received);
} }
#[test]
fn value_transfer() {
// Succeeds in remix (shanghai) but traps the interpreter
let (state, _) = assert_success(&Contract::call_value_transfer(Default::default()), false);
assert_eq!(state.accounts().len(), 2);
assert!(state.accounts().get(&Address::default()).is_some());
}
#[test]
fn echo() {
let (state, address) = State::new_deployed(Contract::call_constructor());
let expected = vec![1, 2, 3, 4, 5];
let contract = Contract::call_call(address, expected.clone());
let (_, output) = state
.transaction()
.with_default_account(&contract.pvm_runtime)
.calldata(contract.calldata)
.call();
assert_eq!(output.flags, ReturnFlags::Success);
let received = alloy_primitives::Bytes::abi_decode(&output.data, true)
.unwrap()
.to_vec();
assert_eq!(expected, received);
}
*/ */
-1
View File
@@ -32,7 +32,6 @@ fn invoke_lld(cmd_args: &[&str]) -> bool {
fn polkavm_linker<T: AsRef<[u8]>>(code: T) -> anyhow::Result<Vec<u8>> { fn polkavm_linker<T: AsRef<[u8]>>(code: T) -> anyhow::Result<Vec<u8>> {
let mut config = polkavm_linker::Config::default(); let mut config = polkavm_linker::Config::default();
config.set_strip(true); config.set_strip(true);
config.set_optimize(true);
polkavm_linker::program_from_elf(config, code.as_ref()) polkavm_linker::program_from_elf(config, code.as_ref())
.map_err(|reason| anyhow::anyhow!("polkavm linker failed: {}", reason)) .map_err(|reason| anyhow::anyhow!("polkavm linker failed: {}", reason))
@@ -21,6 +21,12 @@ pub static GLOBAL_CALLDATA_POINTER: &str = "ptr_calldata";
/// The calldata size global variable name. /// The calldata size global variable name.
pub static GLOBAL_CALLDATA_SIZE: &str = "calldatasize"; pub static GLOBAL_CALLDATA_SIZE: &str = "calldatasize";
/// The return data pointer global variable name.
pub static GLOBAL_RETURN_DATA_POINTER: &str = "ptr_return_data";
/// The return data size pointer global variable name.
pub static GLOBAL_RETURN_DATA_SIZE: &str = "returndatasize";
/// The call flags global variable name. /// The call flags global variable name.
pub static GLOBAL_CALL_FLAGS: &str = "call_flags"; pub static GLOBAL_CALL_FLAGS: &str = "call_flags";
@@ -30,6 +36,15 @@ pub static GLOBAL_CONST_ARRAY_PREFIX: &str = "const_array_";
/// The global verbatim getter identifier prefix. /// The global verbatim getter identifier prefix.
pub static GLOBAL_VERBATIM_GETTER_PREFIX: &str = "get_global::"; pub static GLOBAL_VERBATIM_GETTER_PREFIX: &str = "get_global::";
/// The static word size.
pub static GLOBAL_I256_SIZE: &str = "i256_size";
/// The static value size.
pub static GLOBAL_I160_SIZE: &str = "i160_size";
/// The static i64 size.
pub static GLOBAL_I64_SIZE: &str = "i64_size";
/// The external call data offset in the auxiliary heap. /// The external call data offset in the auxiliary heap.
pub const HEAP_AUX_OFFSET_EXTERNAL_CALL: u64 = 0; pub const HEAP_AUX_OFFSET_EXTERNAL_CALL: u64 = 0;
@@ -19,8 +19,6 @@ pub mod imports {
pub static BLOCK_NUMBER: &str = "block_number"; pub static BLOCK_NUMBER: &str = "block_number";
pub static CHAIN_ID: &str = "chain_id";
pub static CALL: &str = "call"; pub static CALL: &str = "call";
pub static CALLER: &str = "caller"; pub static CALLER: &str = "caller";
@@ -41,9 +39,7 @@ pub mod imports {
pub static RETURN: &str = "seal_return"; pub static RETURN: &str = "seal_return";
pub static RETURNDATACOPY: &str = "return_data_copy"; pub static RETURNDATACOPY: &str = "returndatacopy";
pub static RETURNDATASIZE: &str = "return_data_size";
pub static SET_STORAGE: &str = "set_storage"; pub static SET_STORAGE: &str = "set_storage";
@@ -51,13 +47,12 @@ pub mod imports {
/// All imported runtime API symbols. /// All imported runtime API symbols.
/// Useful for configuring common attributes and linkage. /// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 18] = [ pub static IMPORTS: [&str; 16] = [
ADDRESS, ADDRESS,
BALANCE, BALANCE,
BLOCK_NUMBER, BLOCK_NUMBER,
CALL, CALL,
CALLER, CALLER,
CHAIN_ID,
CODE_SIZE, CODE_SIZE,
DEPOSIT_EVENT, DEPOSIT_EVENT,
GET_STORAGE, GET_STORAGE,
@@ -67,7 +62,6 @@ pub mod imports {
NOW, NOW,
RETURN, RETURN,
RETURNDATACOPY, RETURNDATACOPY,
RETURNDATASIZE,
SET_STORAGE, SET_STORAGE,
VALUE_TRANSFERRED, VALUE_TRANSFERRED,
]; ];
@@ -1,6 +1,7 @@
//! The entry function. //! The entry function.
use inkwell::types::BasicType; use inkwell::types::BasicType;
use inkwell::values::BasicValue;
use crate::polkavm::context::address_space::AddressSpace; use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::runtime; use crate::polkavm::context::function::runtime;
@@ -22,8 +23,11 @@ impl Entry {
/// The number of mandatory arguments. /// The number of mandatory arguments.
pub const MANDATORY_ARGUMENTS_COUNT: usize = 2; pub const MANDATORY_ARGUMENTS_COUNT: usize = 2;
/// Reserve 1kb for calldata. /// Reserve 1mb for calldata.
pub const MAX_CALLDATA_SIZE: usize = 1024; pub const MAX_CALLDATA_SIZE: usize = 1024 * 1024;
/// Reserve 1mb for returndata.
pub const MAX_RETURNDATA_SIZE: usize = 1024 * 1024;
/// Initializes the global variables. /// Initializes the global variables.
/// The pointers are not initialized, because it's not possible to create a null pointer. /// The pointers are not initialized, because it's not possible to create a null pointer.
@@ -39,6 +43,14 @@ impl Entry {
calldata_type.get_undef(), calldata_type.get_undef(),
); );
let returndata_type = context.array_type(context.byte_type(), Self::MAX_RETURNDATA_SIZE);
context.set_global(
crate::polkavm::GLOBAL_RETURN_DATA_POINTER,
returndata_type,
AddressSpace::Stack,
returndata_type.get_undef(),
);
context.set_global( context.set_global(
crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER, crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER,
context.llvm().ptr_type(AddressSpace::Heap.into()), context.llvm().ptr_type(AddressSpace::Heap.into()),
@@ -58,6 +70,12 @@ impl Entry {
AddressSpace::Stack, AddressSpace::Stack,
context.word_undef(), context.word_undef(),
); );
context.set_global(
crate::polkavm::GLOBAL_RETURN_DATA_SIZE,
context.xlen_type(),
AddressSpace::Stack,
context.xlen_type().const_zero().as_basic_value_enum(),
);
context.set_global( context.set_global(
crate::polkavm::GLOBAL_CALL_FLAGS, crate::polkavm::GLOBAL_CALL_FLAGS,
@@ -66,6 +84,33 @@ impl Entry {
context.word_const(0), context.word_const(0),
); );
context.set_global(
crate::polkavm::GLOBAL_I256_SIZE,
context.xlen_type(),
AddressSpace::Stack,
context.integer_const(
crate::polkavm::XLEN,
revive_common::BYTE_LENGTH_X64 as u64 * 4,
),
);
context.set_global(
crate::polkavm::GLOBAL_I160_SIZE,
context.xlen_type(),
AddressSpace::Stack,
context.integer_const(
crate::polkavm::XLEN,
revive_common::BYTE_LENGTH_X64 as u64 * 2 + revive_common::BYTE_LENGTH_X32 as u64,
),
);
context.set_global(
crate::polkavm::GLOBAL_I64_SIZE,
context.xlen_type(),
AddressSpace::Stack,
context.integer_const(crate::polkavm::XLEN, revive_common::BYTE_LENGTH_X64 as u64),
);
Ok(()) Ok(())
} }
@@ -85,7 +130,7 @@ impl Entry {
"input_pointer_casted", "input_pointer_casted",
)?; )?;
let length_pointer = context.build_alloca_at_entry(context.xlen_type(), "len_ptr"); let length_pointer = context.build_alloca(context.xlen_type(), "len_ptr");
let length_pointer_casted = context.builder.build_ptr_to_int( let length_pointer_casted = context.builder.build_ptr_to_int(
length_pointer.value, length_pointer.value,
context.xlen_type(), context.xlen_type(),
+39 -49
View File
@@ -595,53 +595,59 @@ where
self.builder.get_insert_block().expect("Always exists") self.builder.get_insert_block().expect("Always exists")
} }
/// Builds an aligned stack allocation at the function entry. /// Builds a stack allocation instruction.
pub fn build_alloca_at_entry<T: BasicType<'ctx> + Clone + Copy>( /// Sets the alignment to 128 bits.
&self,
r#type: T,
name: &str,
) -> Pointer<'ctx> {
let current_block = self.basic_block();
let entry_block = self.current_function().borrow().entry_block();
match entry_block.get_first_instruction() {
Some(instruction) => self.builder().position_before(&instruction),
None => self.builder().position_at_end(entry_block),
}
let pointer = self.build_alloca(r#type, name);
self.set_basic_block(current_block);
pointer
}
/// Builds an aligned stack allocation at the current position.
/// Use this if [`build_alloca_at_entry`] might change program semantics.
/// Otherwise, alloca should always be built at the function prelude!
pub fn build_alloca<T: BasicType<'ctx> + Clone + Copy>( pub fn build_alloca<T: BasicType<'ctx> + Clone + Copy>(
&self, &self,
r#type: T, r#type: T,
name: &str, name: &str,
) -> Pointer<'ctx> { ) -> Pointer<'ctx> {
let pointer = self.builder.build_alloca(r#type, name).unwrap(); let pointer = self.builder.build_alloca(r#type, name).unwrap();
pointer self.basic_block()
.as_instruction() .get_last_instruction()
.unwrap() .expect("Always exists")
.set_alignment(revive_common::BYTE_LENGTH_STACK_ALIGN as u32) .set_alignment(revive_common::BYTE_LENGTH_STACK_ALIGN as u32)
.expect("Alignment is valid"); .expect("Alignment is valid");
Pointer::new(r#type, AddressSpace::Stack, pointer) Pointer::new(r#type, AddressSpace::Stack, pointer)
} }
/// Load the address at given pointer and zero extend it to the VM word size. /// Allocate an int of size `bit_length` on the stack.
pub fn build_load_address( /// Returns the allocation pointer and the length pointer.
///
/// Useful helper for passing runtime API parameters on the stack.
pub fn build_stack_parameter(
&self,
bit_length: usize,
name: &str,
) -> (Pointer<'ctx>, Pointer<'ctx>) {
let buffer_pointer = self.build_alloca(self.integer_type(bit_length), name);
let symbol = match bit_length {
revive_common::BIT_LENGTH_WORD => GLOBAL_I256_SIZE,
revive_common::BIT_LENGTH_ETH_ADDRESS => GLOBAL_I160_SIZE,
revive_common::BIT_LENGTH_BLOCK_NUMBER => GLOBAL_I64_SIZE,
_ => panic!("invalid stack parameter bit width: {bit_length}"),
};
let length_pointer = self.get_global(symbol).expect("should be declared");
(buffer_pointer, length_pointer.into())
}
/// Load the integer at given pointer and zero extend it to the VM word size.
pub fn build_load_word(
&self, &self,
pointer: Pointer<'ctx>, pointer: Pointer<'ctx>,
bit_length: usize,
name: &str,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> { ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
let address = self.build_byte_swap(self.build_load(pointer, "address_pointer")?)?; let value = self.build_load(
Ok(self pointer.cast(self.integer_type(bit_length)),
.builder() &format!("load_{name}"),
.build_int_z_extend(address.into_int_value(), self.word_type(), "address_zext")? )?;
.into()) let value_extended = self.builder().build_int_z_extend(
value.into_int_value(),
self.word_type(),
&format!("zext_{name}"),
)?;
Ok(value_extended.as_basic_value_enum())
} }
/// Builds a stack load instruction. /// Builds a stack load instruction.
@@ -922,22 +928,6 @@ where
.left() .left()
} }
/// Builds a call to the runtime API `import`, where `import` is a "getter" API.
/// This means that the supplied API method just writes back a single word.
/// `import` is thus expect to have a single parameter, the 32 bytes output buffer,
/// and no return value.
pub fn build_runtime_call_to_getter(
&self,
import: &'static str,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let pointer = self.build_alloca_at_entry(self.word_type(), &format!("{import}_output"));
self.build_runtime_call(import, &[pointer.to_int(self).into()]);
self.build_load(pointer, import)
}
/// Builds a call. /// Builds a call.
pub fn build_call( pub fn build_call(
&self, &self,
+39 -49
View File
@@ -7,14 +7,16 @@ use crate::polkavm::context::Context;
use crate::polkavm::Dependency; use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api; use crate::polkavm_const::runtime_api;
const STATIC_CALL_FLAG: u32 = 0b0001_0000; static STATIC_CALL_FLAG: u32 = 0b0001_0000;
const REENTRANT_CALL_FLAG: u32 = 0b0000_1000;
/// Translates a contract call. /// Translates a contract call.
///
/// If the `simulation_address` is specified, the call is
/// substituted with another instruction according to the specification.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn call<'ctx, D>( pub fn call<'ctx, D>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx, D>,
_gas: inkwell::values::IntValue<'ctx>, gas: inkwell::values::IntValue<'ctx>,
address: inkwell::values::IntValue<'ctx>, address: inkwell::values::IntValue<'ctx>,
value: Option<inkwell::values::IntValue<'ctx>>, value: Option<inkwell::values::IntValue<'ctx>>,
input_offset: inkwell::values::IntValue<'ctx>, input_offset: inkwell::values::IntValue<'ctx>,
@@ -27,71 +29,59 @@ pub fn call<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let address_type = context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS); let address_pointer = context.build_alloca(context.word_type(), "address_ptr");
let address_pointer = context.build_alloca_at_entry(address_type, "address_pointer"); context.build_store(address_pointer, address)?;
let address_truncated =
context
.builder()
.build_int_truncate(address, address_type, "address_truncated")?;
let address_swapped = context.build_byte_swap(address_truncated.into())?;
context.build_store(address_pointer, address_swapped)?;
let value = value.unwrap_or_else(|| context.word_const(0)); let value_pointer = if let Some(value) = value {
let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer"); let value_pointer = context.build_alloca(context.value_type(), "value");
context.build_store(value_pointer, value)?; context.build_store(value_pointer, value)?;
value_pointer
} else {
context.sentinel_pointer()
};
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?; let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
let input_length = context.safe_truncate_int_to_xlen(input_length)?; let input_length = context.safe_truncate_int_to_xlen(input_length)?;
let output_offset = context.safe_truncate_int_to_xlen(output_offset)?; let output_offset = context.safe_truncate_int_to_xlen(output_offset)?;
let output_length = context.safe_truncate_int_to_xlen(output_length)?; let output_length = context.safe_truncate_int_to_xlen(output_length)?;
// TODO: What to supply here? Is there a weight to gas? let gas = context
let _gas = context
.builder() .builder()
.build_int_truncate(_gas, context.integer_type(64), "gas")?; .build_int_truncate(gas, context.integer_type(64), "gas")?;
let flags = if static_call { STATIC_CALL_FLAG } else { 0 };
let input_pointer = context.build_heap_gep(input_offset, input_length)?; let input_pointer = context.build_heap_gep(input_offset, input_length)?;
let output_pointer = context.build_heap_gep(output_offset, output_length)?; let output_pointer = context.build_heap_gep(output_offset, output_length)?;
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length"); let output_length_pointer = context.get_global(crate::polkavm::GLOBAL_RETURN_DATA_SIZE)?;
context.build_store(output_length_pointer, output_length)?; context.build_store(output_length_pointer.into(), output_length)?;
let flags = if static_call { let argument_pointer = revive_runtime_api::calling_convention::Spill::new(
REENTRANT_CALL_FLAG | STATIC_CALL_FLAG
} else {
REENTRANT_CALL_FLAG
};
let flags = context.xlen_type().const_int(flags as u64, false);
let argument_type = revive_runtime_api::calling_convention::call(context.llvm());
let argument_pointer = context.build_alloca_at_entry(argument_type, "call_arguments");
let arguments = &[
flags.as_basic_value_enum(),
address_pointer.value.as_basic_value_enum(),
context.integer_const(64, 0).as_basic_value_enum(),
context.integer_const(64, 0).as_basic_value_enum(),
context.sentinel_pointer().value.as_basic_value_enum(),
value_pointer.value.as_basic_value_enum(),
input_pointer.value.as_basic_value_enum(),
input_length.as_basic_value_enum(),
output_pointer.value.as_basic_value_enum(),
output_length_pointer.value.as_basic_value_enum(),
];
revive_runtime_api::calling_convention::spill(
context.builder(), context.builder(),
argument_pointer.value, revive_runtime_api::calling_convention::call(context.llvm()),
argument_type, "call_arguments",
arguments, )?
)?; .next(context.xlen_type().const_int(flags as u64, false))?
.next(address_pointer.value)?
.next(gas)?
.skip()
.next(context.sentinel_pointer().value)?
.next(value_pointer.value)?
.next(input_pointer.value)?
.next(input_length)?
.next(output_pointer.value)?
.next(output_length_pointer.value)?
.done();
let name = runtime_api::imports::CALL; let name = runtime_api::imports::CALL;
let argument_pointer = context.builder().build_ptr_to_int( let arguments = context.builder().build_ptr_to_int(
argument_pointer.value, argument_pointer,
context.xlen_type(), context.xlen_type(),
"call_argument_pointer", "argument_pointer",
)?; )?;
let success = context let success = context
.build_runtime_call(name, &[argument_pointer.into()]) .build_runtime_call(name, &[arguments.into()])
.unwrap_or_else(|| panic!("{name} should return a value")) .unwrap_or_else(|| panic!("{name} should return a value"))
.into_int_value(); .into_int_value();
+55 -15
View File
@@ -43,7 +43,7 @@ pub fn chain_id<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
context.build_runtime_call_to_getter(runtime_api::imports::CHAIN_ID) Ok(context.word_const(0).as_basic_value_enum())
} }
/// Translates the `block_number` instruction. /// Translates the `block_number` instruction.
@@ -53,7 +53,22 @@ pub fn block_number<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
context.build_runtime_call_to_getter(runtime_api::imports::BLOCK_NUMBER) let (output_pointer, output_length_pointer) = context.build_stack_parameter(
revive_common::BIT_LENGTH_BLOCK_NUMBER,
"block_timestamp_output",
);
context.build_runtime_call(
runtime_api::imports::BLOCK_NUMBER,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
],
);
context.build_load_word(
output_pointer,
revive_common::BIT_LENGTH_BLOCK_NUMBER,
"block_number",
)
} }
/// Translates the `block_timestamp` instruction. /// Translates the `block_timestamp` instruction.
@@ -63,7 +78,22 @@ pub fn block_timestamp<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
context.build_runtime_call_to_getter(runtime_api::imports::NOW) let (output_pointer, output_length_pointer) = context.build_stack_parameter(
revive_common::BIT_LENGTH_BLOCK_TIMESTAMP,
"block_timestamp_output",
);
context.build_runtime_call(
runtime_api::imports::NOW,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
],
);
context.build_load_word(
output_pointer,
revive_common::BIT_LENGTH_BLOCK_TIMESTAMP,
"block_timestamp",
)
} }
/// Translates the `block_hash` instruction. /// Translates the `block_hash` instruction.
@@ -141,15 +171,20 @@ pub fn address<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let pointer = context.build_alloca_at_entry( let (output_pointer, output_length_pointer) =
context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS), context.build_stack_parameter(revive_common::BIT_LENGTH_ETH_ADDRESS, "address_output");
"address_output",
);
context.build_runtime_call( context.build_runtime_call(
runtime_api::imports::ADDRESS, runtime_api::imports::ADDRESS,
&[pointer.to_int(context).into()], &[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
],
); );
context.build_load_address(pointer) let value = context.build_byte_swap(context.build_load(output_pointer, "address")?)?;
Ok(context
.builder()
.build_int_z_extend(value.into_int_value(), context.word_type(), "address_zext")?
.into())
} }
/// Translates the `caller` instruction. /// Translates the `caller` instruction.
@@ -159,13 +194,18 @@ pub fn caller<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let pointer = context.build_alloca_at_entry( let (output_pointer, output_length_pointer) =
context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS), context.build_stack_parameter(revive_common::BIT_LENGTH_ETH_ADDRESS, "caller_output");
"address_output",
);
context.build_runtime_call( context.build_runtime_call(
runtime_api::imports::CALLER, runtime_api::imports::CALLER,
&[pointer.to_int(context).into()], &[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
],
); );
context.build_load_address(pointer) let value = context.build_byte_swap(context.build_load(output_pointer, "caller")?)?;
Ok(context
.builder()
.build_int_z_extend(value.into_int_value(), context.word_type(), "caller_zext")?
.into())
} }
+54 -54
View File
@@ -9,14 +9,26 @@ use crate::polkavm::context::Context;
use crate::polkavm::Dependency; use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api; use crate::polkavm_const::runtime_api;
/// Translates the contract `create` and `create2` instruction. /// Translates the contract `create` instruction.
/// /// The instruction is simulated by a call to a system contract.
/// A `salt` value of `None` is equivalent to `create1`.
pub fn create<'ctx, D>( pub fn create<'ctx, D>(
context: &mut Context<'ctx, D>, context: &mut Context<'ctx, D>,
value: inkwell::values::IntValue<'ctx>, value: 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>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
self::create2(context, value, input_offset, input_length, None)
}
/// Translates the contract `create2` instruction.
pub fn create2<'ctx, D>(
context: &mut Context<'ctx, D>,
value: inkwell::values::IntValue<'ctx>,
input_offset: inkwell::values::IntValue<'ctx>,
input_length: inkwell::values::IntValue<'ctx>,
salt: Option<inkwell::values::IntValue<'ctx>>, salt: Option<inkwell::values::IntValue<'ctx>>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> ) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where where
@@ -25,6 +37,9 @@ where
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?; let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
let input_length = context.safe_truncate_int_to_xlen(input_length)?; let input_length = context.safe_truncate_int_to_xlen(input_length)?;
let value_pointer = context.build_alloca(context.value_type(), "value");
context.build_store(value_pointer, value)?;
let code_hash_pointer = context.build_heap_gep(input_offset, input_length)?; let code_hash_pointer = context.build_heap_gep(input_offset, input_length)?;
let input_data_pointer = context.build_gep( let input_data_pointer = context.build_gep(
@@ -33,68 +48,53 @@ where
.xlen_type() .xlen_type()
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false)], .const_int(revive_common::BYTE_LENGTH_WORD as u64, false)],
context.byte_type(), context.byte_type(),
"input_ptr_parameter_offset", "value_ptr_parameter_offset",
); );
let value_pointer = context.build_alloca_at_entry(context.value_type(), "transferred_value"); let salt_pointer = context.build_alloca(context.word_type(), "salt");
context.build_store(value_pointer, value)?; context.build_store(salt_pointer, salt.unwrap_or_else(|| context.word_const(0)))?;
let salt_pointer = match salt { let (address_pointer, address_length_pointer) =
Some(salt) => { context.build_stack_parameter(revive_common::BIT_LENGTH_ETH_ADDRESS, "address_pointer");
let salt_pointer = context.build_alloca_at_entry(context.word_type(), "salt_pointer");
context.build_store(salt_pointer, salt)?;
salt_pointer
}
None => context.sentinel_pointer(),
};
let address_pointer = context.build_alloca_at_entry(
context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS),
"address_pointer",
);
context.build_store(address_pointer, context.word_const(0))?; context.build_store(address_pointer, context.word_const(0))?;
let argument_type = revive_runtime_api::calling_convention::instantiate(context.llvm()); let argument_pointer = revive_runtime_api::calling_convention::Spill::new(
let argument_pointer = context.build_alloca_at_entry(argument_type, "instantiate_arguments");
let arguments = &[
code_hash_pointer.value.as_basic_value_enum(),
context.integer_const(64, 0).as_basic_value_enum(),
context.integer_const(64, 0).as_basic_value_enum(),
context.sentinel_pointer().value.as_basic_value_enum(),
value_pointer.value.as_basic_value_enum(),
input_data_pointer.value.as_basic_value_enum(),
input_length.as_basic_value_enum(),
address_pointer.value.as_basic_value_enum(),
context.sentinel_pointer().value.as_basic_value_enum(),
context.sentinel_pointer().value.as_basic_value_enum(),
salt_pointer.value.as_basic_value_enum(),
];
revive_runtime_api::calling_convention::spill(
context.builder(), context.builder(),
argument_pointer.value, revive_runtime_api::calling_convention::instantiate(context.llvm()),
argument_type, "create2_arguments",
arguments, )?
)?; .next(code_hash_pointer.value)?
.skip()
.skip()
.next(context.sentinel_pointer().value)?
.next(value_pointer.value)?
.next(input_data_pointer.value)?
.next(input_length)?
.next(address_pointer.value)?
.next(address_length_pointer.value)?
.next(context.sentinel_pointer().value)?
.next(context.sentinel_pointer().value)?
.next(salt_pointer.value)?
.next(
context
.xlen_type()
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false),
)?
.done();
let argument_pointer = context.builder().build_ptr_to_int(
argument_pointer.value,
context.xlen_type(),
"instantiate_argument_pointer",
)?;
context.build_runtime_call( context.build_runtime_call(
runtime_api::imports::INSTANTIATE, runtime_api::imports::INSTANTIATE,
&[argument_pointer.into()], &[context
.builder()
.build_ptr_to_int(argument_pointer, context.xlen_type(), "argument_pointer")?
.into()],
); );
let address = context.build_byte_swap(context.build_load(address_pointer, "address")?)?; context.build_load_word(
Ok(context address_pointer,
.builder() revive_common::BIT_LENGTH_ETH_ADDRESS,
.build_int_z_extend( "address",
address.into_int_value(), )
context.word_type(),
"address_zext",
)?
.into())
} }
/// Translates the contract hash instruction, which is actually used to set the hash of the contract /// Translates the contract hash instruction, which is actually used to set the hash of the contract
@@ -23,13 +23,20 @@ pub fn value<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let output_pointer = context.build_alloca(context.value_type(), "value_transferred"); let (output_pointer, output_length_pointer) =
context.build_store(output_pointer, context.word_const(0))?; context.build_stack_parameter(revive_common::BIT_LENGTH_VALUE, "value_transferred_output");
context.build_runtime_call( context.build_runtime_call(
runtime_api::imports::VALUE_TRANSFERRED, runtime_api::imports::VALUE_TRANSFERRED,
&[output_pointer.to_int(context).into()], &[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
],
); );
context.build_load(output_pointer, "value_transferred") context.build_load_word(
output_pointer,
revive_common::BIT_LENGTH_VALUE,
"value_transferred",
)
} }
/// Translates the `balance` instructions. /// Translates the `balance` instructions.
@@ -49,32 +56,16 @@ where
context.xlen_type(), context.xlen_type(),
"balance", "balance",
)?; )?;
let _address = context.builder().build_ptr_to_int( let address = context.builder().build_ptr_to_int(
address_pointer.value, address_pointer.value,
context.xlen_type(), context.xlen_type(),
"address", "address",
)?; )?;
context.build_runtime_call(runtime_api::imports::BALANCE, &[balance.into()]); context.build_runtime_call(
runtime_api::imports::BALANCE,
context.build_load(balance_pointer, "balance") &[address.into(), balance.into()],
} );
/// Translates the `selfbalance` instructions.
pub fn self_balance<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let balance_pointer = context.build_alloca(context.word_type(), "balance_pointer");
let balance = context.builder().build_ptr_to_int(
balance_pointer.value,
context.xlen_type(),
"balance",
)?;
context.build_runtime_call(runtime_api::imports::BALANCE, &[balance.into()]);
context.build_load(balance_pointer, "balance") context.build_load(balance_pointer, "balance")
} }
+1 -3
View File
@@ -43,7 +43,6 @@ where
context.byte_type().array_type(topics_buffer_size as u32), context.byte_type().array_type(topics_buffer_size as u32),
"topics_buffer", "topics_buffer",
); );
for (n, topic) in topics.iter().enumerate() { for (n, topic) in topics.iter().enumerate() {
let topic_buffer_offset = context let topic_buffer_offset = context
.xlen_type() .xlen_type()
@@ -58,7 +57,6 @@ where
context.build_byte_swap(topic.as_basic_value_enum())?, context.build_byte_swap(topic.as_basic_value_enum())?,
)?; )?;
} }
[ [
context context
.builder() .builder()
@@ -70,7 +68,7 @@ where
.as_basic_value_enum(), .as_basic_value_enum(),
context context
.xlen_type() .xlen_type()
.const_int(topics.len() as u64, false) .const_int(topics_buffer_size as u64, false)
.as_basic_value_enum(), .as_basic_value_enum(),
input_pointer.as_basic_value_enum(), input_pointer.as_basic_value_enum(),
input_length.as_basic_value_enum(), input_length.as_basic_value_enum(),
@@ -1,5 +1,7 @@
//! Translates the return data instructions. //! Translates the return data instructions.
use inkwell::values::BasicValue;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency; use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api; use crate::polkavm_const::runtime_api;
@@ -11,17 +13,13 @@ pub fn size<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let output_pointer = context.build_alloca_at_entry(context.word_type(), "return_data_size"); let value = context
let output_pointer_parameter = context.builder().build_ptr_to_int( .get_global_value(crate::polkavm::GLOBAL_RETURN_DATA_SIZE)?
output_pointer.value, .into_int_value();
context.xlen_type(), Ok(context
"return_data_copy_output_pointer", .builder()
)?; .build_int_z_extend(value, context.word_type(), "calldatasize_extended")?
context.build_runtime_call( .as_basic_value_enum())
runtime_api::imports::RETURNDATASIZE,
&[output_pointer_parameter.into()],
);
context.build_load(output_pointer, "return_data_size_load")
} }
/// Translates the return data copy, trapping if /// Translates the return data copy, trapping if
@@ -41,49 +39,16 @@ where
let destination_offset = context.safe_truncate_int_to_xlen(destination_offset)?; let destination_offset = context.safe_truncate_int_to_xlen(destination_offset)?;
let size = context.safe_truncate_int_to_xlen(size)?; let size = context.safe_truncate_int_to_xlen(size)?;
let output_pointer = context.builder().build_ptr_to_int( let destination_offset = context.builder().build_ptr_to_int(
context.build_heap_gep(destination_offset, size)?.value, context.build_heap_gep(destination_offset, size)?.value,
context.xlen_type(), context.xlen_type(),
"return_data_copy_output_pointer", "destination_offset",
)?;
let output_length_pointer = context.build_alloca_at_entry(
context.xlen_type(),
"return_data_copy_output_length_pointer",
);
context.build_store(output_length_pointer, size)?;
let output_length_pointer_int = context.builder().build_ptr_to_int(
output_length_pointer.value,
context.xlen_type(),
"return_data_copy_output_length_pointer_int",
)?; )?;
context.build_runtime_call( context.build_runtime_call(
runtime_api::imports::RETURNDATACOPY, runtime_api::imports::RETURNDATACOPY,
&[ &[destination_offset.into(), source_offset.into(), size.into()],
output_pointer.into(),
output_length_pointer_int.into(),
source_offset.into(),
],
); );
// Trap on OOB (will be different in EOF code)
let overflow_block = context.append_basic_block("return_data_overflow");
let non_overflow_block = context.append_basic_block("return_data_non_overflow");
let is_overflow = context.builder().build_int_compare(
inkwell::IntPredicate::UGT,
size,
context
.build_load(output_length_pointer, "bytes_written")?
.into_int_value(),
"is_overflow",
)?;
context.build_conditional_branch(is_overflow, overflow_block, non_overflow_block)?;
context.set_basic_block(overflow_block);
context.build_call(context.intrinsics().trap, &[], "invalid_trap");
context.build_unreachable();
context.set_basic_block(non_overflow_block);
Ok(()) Ok(())
} }
@@ -39,9 +39,9 @@ impl TargetMachine {
/// LLVM target features. /// LLVM target features.
#[cfg(feature = "riscv-zbb")] #[cfg(feature = "riscv-zbb")]
pub const VM_FEATURES: &'static str = "+zbb,+e,+m,+c"; pub const VM_FEATURES: &'static str = "+zbb,+e,+m";
#[cfg(not(feature = "riscv-zbb"))] #[cfg(not(feature = "riscv-zbb"))]
pub const VM_FEATURES: &'static str = "+e,+m,+c"; pub const VM_FEATURES: &'static str = "+e,+m";
/// A shortcut constructor. /// A shortcut constructor.
/// A separate instance for every optimization level is created. /// A separate instance for every optimization level is created.
Binary file not shown.
+1 -3
View File
@@ -75,12 +75,10 @@ impl pallet_revive::Config for Runtime {
type DepositPerByte = DepositPerByte; type DepositPerByte = DepositPerByte;
type DepositPerItem = DepositPerItem; type DepositPerItem = DepositPerItem;
type AddressMapper = AccountId; type AddressMapper = AccountId;
type RuntimeMemory = ConstU32<{ 512 * 1024 * 1024 }>;
type PVFMemory = ConstU32<{ 1024 * 1024 * 1024 }>;
type UnsafeUnstableInterface = UnstableInterface; type UnsafeUnstableInterface = UnstableInterface;
type UploadOrigin = EnsureSigned<AccountId32>; type UploadOrigin = EnsureSigned<AccountId32>;
type InstantiateOrigin = EnsureSigned<AccountId32>; type InstantiateOrigin = EnsureSigned<AccountId32>;
type Migrations = ();
type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
type Debug = (); type Debug = ();
type ChainId = ConstU64<420_420_420>;
} }
+21 -59
View File
@@ -58,6 +58,7 @@ pub enum SpecsAction {
}, },
/// Verify the result of the last call, omitting this will simply ensure the last call was successful /// Verify the result of the last call, omitting this will simply ensure the last call was successful
VerifyCall(VerifyCallExpectation), VerifyCall(VerifyCallExpectation),
/// Verify the balance of an account /// Verify the balance of an account
VerifyBalance { VerifyBalance {
origin: TestAddress, origin: TestAddress,
@@ -178,11 +179,7 @@ impl Default for Specs {
fn default() -> Self { fn default() -> Self {
Self { Self {
differential: false, differential: false,
balances: vec![ balances: vec![(ALICE, 1_000_000_000)],
(ALICE, 1_000_000_000),
(BOB, 1_000_000_000),
(CHARLIE, 1_000_000_000),
],
actions: Default::default(), actions: Default::default(),
} }
} }
@@ -275,10 +272,10 @@ impl Specs {
origin, origin,
value, value,
gas_limit, gas_limit,
storage_deposit_limit,
code, code,
data, data,
salt, salt,
..
} => { } => {
let Code::Solidity { let Code::Solidity {
path: Some(path), path: Some(path),
@@ -289,41 +286,21 @@ impl Specs {
else { else {
panic!("the differential runner requires Code::Solidity source"); panic!("the differential runner requires Code::Solidity source");
}; };
assert_ne!(solc_optimizer, Some(false), "solc_optimizer must be enabled in differntial mode");
assert_ne!( assert_ne!(pipeline, Some(revive_solidity::SolcPipeline::EVMLA), "yul pipeline must be enabled in differntial mode");
solc_optimizer, assert!(storage_deposit_limit.is_none(), "storage deposit limit is not supported in differential mode");
Some(false), assert!(salt.0.is_none(), "salt is not supported in differential mode");
"solc_optimizer must be enabled in differntial mode" assert_eq!(origin, TestAddress::default(), "configuring the origin is not supported in differential mode");
);
assert_ne!(
pipeline,
Some(revive_solidity::SolcPipeline::EVMLA),
"yul pipeline must be enabled in differntial mode"
);
assert!(
salt.0.is_none(),
"salt is not supported in differential mode"
);
assert_eq!(
origin,
TestAddress::default(),
"configuring the origin is not supported in differential mode"
);
let deploy_code = match std::fs::read_to_string(&path) { let deploy_code = match std::fs::read_to_string(&path) {
Ok(solidity_source) => { Ok(solidity_source) => compile_evm_deploy_code(&contract, &solidity_source),
hex::encode(compile_evm_deploy_code(&contract, &solidity_source))
}
Err(err) => panic!( Err(err) => panic!(
"failed to read solidity source\n . path: '{}'\n . error: {:?}", "failed to read solidity source\n . path: '{}'\n . error: {:?}",
path.display(), path.display(),
err err
), ),
}; };
let mut vm = evm let deploy_code = hex::encode(deploy_code);
.code_blob(deploy_code.as_bytes().to_vec()) let mut vm = evm.code_blob(deploy_code.as_bytes().to_vec()).sender(origin.to_eth_addr(&[]).0.into()).deploy(true);
.sender(origin.to_eth_addr(&[]).0.into())
.deploy(true);
if !data.is_empty() { if !data.is_empty() {
vm = vm.input(data.into()); vm = vm.input(data.into());
} }
@@ -338,13 +315,7 @@ impl Specs {
let deployed_account = log.account_deployed.expect("no account was created"); let deployed_account = log.account_deployed.expect("no account was created");
let account_pvm = TestAddress::Instantiated(deployed_accounts.len()); let account_pvm = TestAddress::Instantiated(deployed_accounts.len());
deployed_accounts.push(deployed_account); deployed_accounts.push(deployed_account);
derived_specs derived_specs.actions.append(&mut SpecsAction::derive_verification(&log, deployed_account, account_pvm));
.actions
.append(&mut SpecsAction::derive_verification(
&log,
deployed_account,
account_pvm,
));
evm = Evm::from_genesis(log.state_dump.into()); evm = Evm::from_genesis(log.state_dump.into());
} }
Call { Call {
@@ -352,23 +323,16 @@ impl Specs {
dest, dest,
value, value,
gas_limit, gas_limit,
storage_deposit_limit,
data, data,
..
} => { } => {
assert_eq!( assert_eq!(origin, TestAddress::default(), "configuring the origin is not supported in differential mode");
origin, assert!(storage_deposit_limit.is_none(), "storage deposit limit is not supported in differential mode");
TestAddress::default(),
"configuring the origin is not supported in differential mode"
);
let TestAddress::Instantiated(n) = dest else { let TestAddress::Instantiated(n) = dest else {
panic!("the differential runner requires TestAccountId::Instantiated(n) as dest"); panic!("the differential runner requires TestAccountId::Instantiated(n) as dest");
}; };
let address = deployed_accounts let address = deployed_accounts.get(n).unwrap_or_else(|| panic!("no account at index {n} "));
.get(n) let mut vm = evm.receiver(*address).sender(origin.to_eth_addr(&[]).0.into());
.unwrap_or_else(|| panic!("no account at index {n} "));
let mut vm = evm
.receiver(*address)
.sender(origin.to_eth_addr(&[]).0.into());
if !data.is_empty() { if !data.is_empty() {
vm = vm.input(data.into()); vm = vm.input(data.into());
} }
@@ -380,13 +344,10 @@ impl Specs {
} }
let log = vm.run(); let log = vm.run();
derived_specs derived_specs.actions.append(&mut SpecsAction::derive_verification(&log, *address, dest));
.actions
.append(&mut SpecsAction::derive_verification(&log, *address, dest));
evm = Evm::from_genesis(log.state_dump.into()); evm = Evm::from_genesis(log.state_dump.into());
} }
Upload { .. } => continue, _ => panic!("only instantiate and call action allowed in differential mode, got: {action:?}"),
other => derived_specs.actions.push(other),
} }
} }
@@ -441,7 +402,7 @@ impl Specs {
pallet_revive::Code::Existing(_) => continue, pallet_revive::Code::Existing(_) => continue,
pallet_revive::Code::Upload(bytes) => bytes, pallet_revive::Code::Upload(bytes) => bytes,
}, },
storage_deposit_limit.unwrap_or(DEPOSIT_LIMIT), storage_deposit_limit.unwrap_or_default(),
) )
.unwrap_or_else(|error| panic!("code upload failed: {error:?}")), .unwrap_or_else(|error| panic!("code upload failed: {error:?}")),
Call { Call {
@@ -481,6 +442,7 @@ impl Specs {
expected, expected,
} => { } => {
let address = contract.to_eth_addr(&results); let address = contract.to_eth_addr(&results);
dbg!(contract.to_account_id(&results));
let Ok(value) = Contracts::get_storage(address, key) else { let Ok(value) = Contracts::get_storage(address, key) else {
panic!("error reading storage for address {address}"); panic!("error reading storage for address {address}");
}; };
+51 -25
View File
@@ -3,7 +3,7 @@ use inkwell::{
context::Context, context::Context,
module::Module, module::Module,
types::{BasicType, StructType}, types::{BasicType, StructType},
values::{BasicValueEnum, PointerValue}, values::{BasicValue, PointerValue},
}; };
/// Creates a module that sets the PolkaVM minimum stack size to [`size`] if linked in. /// Creates a module that sets the PolkaVM minimum stack size to [`size`] if linked in.
@@ -21,33 +21,56 @@ pub fn min_stack_size<'context>(
module module
} }
/// Helper for building function calls with stack spilled arguments. pub struct Spill<'a, 'ctx> {
/// - `pointer`: points to a struct of the packed argument struct type
/// - `type`: the packed argument struct type
/// - `arguments`: a correctly ordered list of the struct field values
pub fn spill<'ctx>(
builder: &Builder<'ctx>,
pointer: PointerValue<'ctx>, pointer: PointerValue<'ctx>,
builder: &'a Builder<'ctx>,
r#type: StructType<'ctx>, r#type: StructType<'ctx>,
arguments: &[BasicValueEnum<'ctx>], current_field: u32,
) -> anyhow::Result<()> { }
for index in 0..r#type.get_field_types().len() {
let field_pointer = builder.build_struct_gep( impl<'a, 'ctx> Spill<'a, 'ctx> {
r#type, pub fn new(
pointer, builder: &'a Builder<'ctx>,
index as u32, r#type: StructType<'ctx>,
&format!("spill_parameter_{}", index), name: &str,
)?; ) -> anyhow::Result<Self> {
let field_value = arguments Ok(Self {
.get(index) pointer: builder.build_alloca(r#type, name)?,
.ok_or_else(|| anyhow::anyhow!("invalid index {index} for struct type {}", r#type))?; builder,
builder.build_store(field_pointer, *field_value)?; r#type,
} current_field: 0,
})
Ok(()) }
pub fn next<V: BasicValue<'ctx>>(mut self, value: V) -> anyhow::Result<Self> {
let field_pointer = self.builder.build_struct_gep(
self.r#type,
self.pointer,
self.current_field,
&format!("spill_parameter_{}", self.current_field),
)?;
self.builder.build_store(field_pointer, value)?;
self.current_field += 1;
Ok(self)
}
pub fn skip(mut self) -> Self {
self.current_field += 1;
self
}
pub fn done(self) -> PointerValue<'ctx> {
assert!(
self.r#type
.get_field_type_at_index(self.current_field)
.is_none(),
"there must not be any missing parameters"
);
self.pointer
}
} }
/// Returns a packed struct argument type for the `instantiate` API.
pub fn instantiate(context: &Context) -> StructType { pub fn instantiate(context: &Context) -> StructType {
context.struct_type( context.struct_type(
&[ &[
@@ -67,18 +90,21 @@ pub fn instantiate(context: &Context) -> StructType {
context.i32_type().as_basic_type_enum(), context.i32_type().as_basic_type_enum(),
// address_ptr: u32, // address_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(), context.ptr_type(Default::default()).as_basic_type_enum(),
// address_len_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
// output_ptr: u32, // output_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(), context.ptr_type(Default::default()).as_basic_type_enum(),
// output_len_ptr: u32, // output_len_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(), context.ptr_type(Default::default()).as_basic_type_enum(),
// salt_ptr: u32, // salt_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(), context.ptr_type(Default::default()).as_basic_type_enum(),
// salt_len: u32
context.i32_type().as_basic_type_enum(),
], ],
true, true,
) )
} }
/// Returns a packed struct argument type for the `call` API.
pub fn call(context: &Context) -> StructType { pub fn call(context: &Context) -> StructType {
context.struct_type( context.struct_type(
&[ &[
+7 -11
View File
@@ -55,11 +55,9 @@ POLKAVM_IMPORT(void, input, uint32_t, uint32_t)
POLKAVM_IMPORT(void, seal_return, uint32_t, uint32_t, uint32_t) POLKAVM_IMPORT(void, seal_return, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, return_data_copy, uint32_t, uint32_t, uint32_t) POLKAVM_IMPORT(void, returndatacopy, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, return_data_size, uint32_t) POLKAVM_IMPORT(void, value_transferred, uint32_t, uint32_t)
POLKAVM_IMPORT(void, value_transferred, uint32_t)
POLKAVM_IMPORT(uint32_t, set_storage, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) POLKAVM_IMPORT(uint32_t, set_storage, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t)
@@ -79,7 +77,7 @@ POLKAVM_IMPORT(uint32_t, instantiate, uint32_t)
POLKAVM_IMPORT(void, terminate, uint32_t) POLKAVM_IMPORT(void, terminate, uint32_t)
POLKAVM_IMPORT(void, caller, uint32_t) POLKAVM_IMPORT(void, caller, uint32_t, uint32_t)
POLKAVM_IMPORT(uint32_t, is_contract, uint32_t) POLKAVM_IMPORT(uint32_t, is_contract, uint32_t)
@@ -93,23 +91,21 @@ POLKAVM_IMPORT(uint32_t, caller_is_origin)
POLKAVM_IMPORT(uint32_t, caller_is_root) POLKAVM_IMPORT(uint32_t, caller_is_root)
POLKAVM_IMPORT(void, address, uint32_t) POLKAVM_IMPORT(void, address, uint32_t, uint32_t)
POLKAVM_IMPORT(void, weight_to_fee, uint64_t, uint32_t, uint32_t) POLKAVM_IMPORT(void, weight_to_fee, uint64_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, gas_left, uint32_t, uint32_t) POLKAVM_IMPORT(void, gas_left, uint32_t, uint32_t)
POLKAVM_IMPORT(void, balance, uint32_t) POLKAVM_IMPORT(void, balance, uint32_t, uint32_t)
POLKAVM_IMPORT(void, chain_id, uint32_t) POLKAVM_IMPORT(void, now, uint32_t, uint32_t)
POLKAVM_IMPORT(void, now, uint32_t)
POLKAVM_IMPORT(void, minimum_balance, uint32_t, uint32_t) POLKAVM_IMPORT(void, minimum_balance, uint32_t, uint32_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)
POLKAVM_IMPORT(void, block_number, uint32_t) POLKAVM_IMPORT(void, block_number, uint32_t, uint32_t)
POLKAVM_IMPORT(void, hash_sha2_256, uint32_t, uint32_t, uint32_t) POLKAVM_IMPORT(void, hash_sha2_256, uint32_t, uint32_t, uint32_t)
@@ -1119,7 +1119,6 @@ where
value, value,
input_offset, input_offset,
input_length, input_length,
None,
) )
.map(Some) .map(Some)
} }
@@ -1131,7 +1130,7 @@ where
let input_length = arguments[2].into_int_value(); let input_length = arguments[2].into_int_value();
let salt = arguments[3].into_int_value(); let salt = arguments[3].into_int_value();
revive_llvm_context::polkavm_evm_create::create( revive_llvm_context::polkavm_evm_create::create2(
context, context,
value, value,
input_offset, input_offset,
@@ -1160,9 +1159,7 @@ where
let address = arguments[0].into_int_value(); let address = arguments[0].into_int_value();
revive_llvm_context::polkavm_evm_ether_gas::balance(context, address).map(Some) revive_llvm_context::polkavm_evm_ether_gas::balance(context, address).map(Some)
} }
InstructionName::SELFBALANCE => { InstructionName::SELFBALANCE => todo!(),
revive_llvm_context::polkavm_evm_ether_gas::self_balance(context).map(Some)
}
InstructionName::GASLIMIT => { InstructionName::GASLIMIT => {
revive_llvm_context::polkavm_evm_contract_context::gas_limit(context).map(Some) revive_llvm_context::polkavm_evm_contract_context::gas_limit(context).map(Some)
+3 -3
View File
@@ -79,7 +79,7 @@ pub struct Arguments {
pub solc: Option<String>, pub solc: Option<String>,
/// The EVM target version to generate IR for. /// The EVM target version to generate IR for.
/// See https://github.com/paritytech/revive/blob/main/crates/common/src/evm_version.rs for reference. /// See https://github.com/xermicus/revive/blob/main/crates/common/src/evm_version.rs for reference.
#[structopt(long = "evm-version")] #[structopt(long = "evm-version")]
pub evm_version: Option<String>, pub evm_version: Option<String>,
@@ -196,14 +196,14 @@ impl Arguments {
} }
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
if self.recursive_process_input.is_some() && !self.recursive_process { if self.recursive_process_input.is_none() && !self.recursive_process {
anyhow::bail!("--process-input can be only used when --recursive-process is given"); anyhow::bail!("--process-input can be only used when --recursive-process is given");
} }
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
if self.recursive_process if self.recursive_process
&& ((self.recursive_process_input.is_none() && std::env::args().count() > 2) && ((self.recursive_process_input.is_none() && std::env::args().count() > 2)
|| (self.recursive_process_input.is_some() && std::env::args().count() > 4)) || (self.recursive_process_input.is_none() && std::env::args().count() > 4))
{ {
anyhow::bail!("No other options are allowed in recursive mode."); anyhow::bail!("No other options are allowed in recursive mode.");
} }
+1 -1
View File
@@ -37,7 +37,7 @@ impl Compiler {
pub const FIRST_VIA_IR_VERSION: semver::Version = semver::Version::new(0, 8, 13); pub const FIRST_VIA_IR_VERSION: semver::Version = semver::Version::new(0, 8, 13);
/// The last supported version of `solc`. /// The last supported version of `solc`.
pub const LAST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 27); pub const LAST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 26);
/// A shortcut constructor. /// A shortcut constructor.
/// Different tools may use different `executable` names. For example, the integration tester /// Different tools may use different `executable` names. For example, the integration tester
@@ -3,7 +3,7 @@
"version": "1.0.0", "version": "1.0.0",
"title": "resolc CLI Tests", "title": "resolc CLI Tests",
"description": "Auto tests for verifying resolc CLI", "description": "Auto tests for verifying resolc CLI",
"repository": "https://github.com/paritytech/revive", "repository": "https://github.com/xermicus/revive",
"main": "index.js", "main": "index.js",
"private": true, "private": true,
"scripts": { "scripts": {
@@ -831,7 +831,6 @@ impl FunctionCall {
value, value,
input_offset, input_offset,
input_length, input_length,
None,
) )
.map(Some) .map(Some)
} }
@@ -843,7 +842,7 @@ impl FunctionCall {
let input_length = arguments[2].into_int_value(); let input_length = arguments[2].into_int_value();
let salt = arguments[3].into_int_value(); let salt = arguments[3].into_int_value();
revive_llvm_context::polkavm_evm_create::create( revive_llvm_context::polkavm_evm_create::create2(
context, context,
value, value,
input_offset, input_offset,
@@ -913,9 +912,7 @@ impl FunctionCall {
let address = arguments[0].into_int_value(); let address = arguments[0].into_int_value();
revive_llvm_context::polkavm_evm_ether_gas::balance(context, address).map(Some) revive_llvm_context::polkavm_evm_ether_gas::balance(context, address).map(Some)
} }
Name::SelfBalance => { Name::SelfBalance => todo!(),
revive_llvm_context::polkavm_evm_ether_gas::self_balance(context).map(Some)
}
Name::GasLimit => { Name::GasLimit => {
revive_llvm_context::polkavm_evm_contract_context::gas_limit(context).map(Some) revive_llvm_context::polkavm_evm_contract_context::gas_limit(context).map(Some)
-7
View File
@@ -1,7 +0,0 @@
#! /usr/bin/env bash
CONTAINER=revive-builder-debian-x86
VERSION=latest
DOCKERFILE=revive-builder-debian.dockerfile
docker build --rm -t ${CONTAINER}:${VERSION} -f ${DOCKERFILE} $@
-20
View File
@@ -1,20 +0,0 @@
#! /usr/bin/env bash
set -euo pipefail
REVIVE_INSTALL_DIR=$(pwd)/target/release
while getopts "o:" option ; do
case $option in
o) # Output directory
REVIVE_INSTALL_DIR=$OPTARG
;;
\?) echo "Error: Invalid option"
exit 1;;
esac
done
echo "Installing to ${REVIVE_INSTALL_DIR}"
$(pwd)/build-llvm.sh
export PATH=$(pwd)/llvm18.0/bin:$PATH
make install-revive REVIVE_INSTALL_DIR=${REVIVE_INSTALL_DIR}
-14
View File
@@ -1,14 +0,0 @@
# syntax=docker/dockerfile:1
# Dockerfile for building revive in a Debian container.
FROM debian:12
RUN <<EOF
apt-get update
apt-get install -q -y build-essential cmake make ninja-build python3 \
libmpfr-dev libgmp-dev libmpc-dev ncurses-dev \
git curl
EOF
ARG RUST_VERSION=stable
RUN <<EOF
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain ${RUST_VERSION}
EOF
ENV PATH=/root/.cargo/bin:${PATH}
-6
View File
@@ -1,6 +0,0 @@
#! /usr/bin/env bash
CONTAINER=revive-builder-debian-x86
VERSION=latest
docker run --rm -v $(pwd):$(pwd) -w $(pwd) ${CONTAINER}:${VERSION} $@