mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-22 04:27:58 +00:00
25ee4eef5a
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
388 lines
12 KiB
Rust
388 lines
12 KiB
Rust
use alloy_primitives::{Address, Bytes, I256, U256};
|
|
use alloy_sol_types::{sol, SolCall, SolConstructor};
|
|
|
|
use resolc::test_utils::*;
|
|
use revive_llvm_context::OptimizerSettings;
|
|
|
|
#[derive(Clone)]
|
|
pub struct Contract {
|
|
pub name: &'static str,
|
|
pub evm_runtime: Vec<u8>,
|
|
pub pvm_runtime: Vec<u8>,
|
|
pub calldata: Vec<u8>,
|
|
pub yul: String,
|
|
}
|
|
|
|
impl Contract {
|
|
pub fn build(calldata: Vec<u8>, name: &'static str, code: &str) -> Self {
|
|
Self {
|
|
name,
|
|
evm_runtime: compile_evm_bin_runtime(name, code),
|
|
pvm_runtime: compile_blob(name, code),
|
|
calldata,
|
|
yul: compile_to_yul(name, code, true),
|
|
}
|
|
}
|
|
|
|
pub fn build_size_opt(calldata: Vec<u8>, name: &'static str, code: &str) -> Self {
|
|
Self {
|
|
name,
|
|
evm_runtime: compile_evm_bin_runtime(name, code),
|
|
pvm_runtime: compile_blob_with_options(name, code, true, OptimizerSettings::size()),
|
|
calldata,
|
|
yul: compile_to_yul(name, code, true),
|
|
}
|
|
}
|
|
}
|
|
|
|
macro_rules! case {
|
|
// Arguments:
|
|
// 1. The file name, expect to live under "../contracts/"
|
|
// 2. The Solidity contract name
|
|
// 3. The derived Solidity function call name
|
|
// 4. The method name on [Contract]
|
|
// 5. Any parameters to the Solidity functions
|
|
($file_name:literal, $contract_name:ident, $contract_method:ident, $method_name:ident, $( $v:ident: $t:ty ),* ) => {
|
|
impl Contract {
|
|
pub fn $method_name($($v: $t),*) -> Self {
|
|
let code = include_str!(concat!("../contracts/", $file_name));
|
|
let args = $contract_name::$contract_method::new(($($v,)*)).abi_encode();
|
|
let name = stringify!($contract_name);
|
|
Contract::build(args, name, code)
|
|
}
|
|
}
|
|
};
|
|
|
|
// Arguments:
|
|
// 1. The file name, expect to live under "../contracts/"
|
|
// 2. The Solidity contract name
|
|
// 3. Raw Calldata
|
|
// 4. The method name on [Contract]
|
|
($file_name:literal, $contract_name:literal, $calldata:expr, $method_name:ident) => {
|
|
impl Contract {
|
|
pub fn $method_name() -> Self {
|
|
let code = include_str!(concat!("../contracts/", $file_name));
|
|
Contract::build($calldata, $contract_name, code)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
case!("Create.sol", "CreateA", vec![0; 4], create_a);
|
|
case!("Create.sol", "CreateB", vec![0; 4], create_b);
|
|
|
|
sol!(contract Baseline { function baseline() public payable; });
|
|
case!("Baseline.sol", Baseline, baselineCall, baseline,);
|
|
|
|
sol!(contract Flipper {
|
|
constructor (bool);
|
|
|
|
function flip() public;
|
|
});
|
|
case!("flipper.sol", Flipper, flipCall, flipper,);
|
|
case!("flipper.sol", Flipper, constructorCall, flipper_constructor, coin: bool);
|
|
|
|
sol!(contract Computation {
|
|
function odd_product(int32 n) public pure returns (int64);
|
|
function triangle_number(int64 n) public pure returns (int64 sum);
|
|
});
|
|
case!("Computation.sol", Computation, odd_productCall, odd_product, n: i32);
|
|
case!("Computation.sol", Computation, triangle_numberCall, triangle_number, n: i64);
|
|
|
|
sol!(
|
|
contract FibonacciRecursive {
|
|
function fib3(uint n) public pure returns (uint);
|
|
}
|
|
);
|
|
case!("Fibonacci.sol", FibonacciRecursive, fib3Call, fib_recursive, n: U256);
|
|
|
|
sol!(
|
|
contract FibonacciIterative {
|
|
function fib3(uint n) external pure returns (uint b);
|
|
}
|
|
);
|
|
case!("Fibonacci.sol", FibonacciIterative, fib3Call, fib_iterative, n: U256);
|
|
|
|
sol!(
|
|
contract FibonacciBinet {
|
|
function fib3(uint n) external pure returns (uint a);
|
|
}
|
|
);
|
|
case!("Fibonacci.sol", FibonacciBinet, fib3Call, fib_binet, n: U256);
|
|
|
|
sol!(
|
|
contract SHA1 {
|
|
function sha1(bytes memory data) public pure returns (bytes20 ret);
|
|
}
|
|
);
|
|
case!("SHA1.sol", SHA1, sha1Call, sha1, pre: Bytes);
|
|
|
|
sol!(
|
|
contract ERC20 {
|
|
function totalSupply() external view returns (uint);
|
|
|
|
function balanceOf(address account) external view returns (uint);
|
|
|
|
function transfer(address recipient, uint amount) external returns (bool);
|
|
|
|
function allowance(
|
|
address owner,
|
|
address spender
|
|
) external view returns (uint);
|
|
|
|
function approve(address spender, uint amount) external returns (bool);
|
|
|
|
function transferFrom(
|
|
address sender,
|
|
address recipient,
|
|
uint amount
|
|
) external returns (bool);
|
|
|
|
event Transfer(address indexed from, address indexed to, uint value);
|
|
event Approval(address indexed owner, address indexed spender, uint value);
|
|
}
|
|
);
|
|
case!("ERC20.sol", ERC20, totalSupplyCall, erc20,);
|
|
|
|
sol!(
|
|
contract Block {
|
|
function timestamp() public view returns (uint ret);
|
|
|
|
function number() public view returns (uint ret);
|
|
}
|
|
);
|
|
case!("Block.sol", Block, numberCall, block_number,);
|
|
case!("Block.sol", Block, timestampCall, block_timestamp,);
|
|
|
|
sol!(
|
|
contract Context {
|
|
function address_this() public view returns (address);
|
|
|
|
function caller() public pure returns (address);
|
|
}
|
|
);
|
|
case!("Context.sol", Context, address_thisCall, context_address,);
|
|
case!("Context.sol", Context, callerCall, context_caller,);
|
|
|
|
sol!(
|
|
contract DivisionArithmetics {
|
|
function div(uint n, uint d) public pure returns (uint q);
|
|
|
|
function sdiv(int n, int d) public pure returns (int q);
|
|
|
|
function mod(uint n, uint d) public pure returns (uint r);
|
|
|
|
function smod(int n, int d) public pure returns (int r);
|
|
}
|
|
);
|
|
case!("DivisionArithmetics.sol", DivisionArithmetics, divCall, division_arithmetics_div, n: U256, d: U256);
|
|
case!("DivisionArithmetics.sol", DivisionArithmetics, sdivCall, division_arithmetics_sdiv, n: I256, d: I256);
|
|
case!("DivisionArithmetics.sol", DivisionArithmetics, modCall, division_arithmetics_mod, n: U256, d: U256);
|
|
case!("DivisionArithmetics.sol", DivisionArithmetics, smodCall, division_arithmetics_smod, n: I256, d: I256);
|
|
|
|
sol!(
|
|
contract Send {
|
|
function transfer_self(uint _amount) public payable;
|
|
}
|
|
);
|
|
case!("Send.sol", Send, transfer_selfCall, send_self, amount: U256);
|
|
|
|
sol!(
|
|
contract Transfer {
|
|
function transfer_self(uint _amount) public payable;
|
|
}
|
|
);
|
|
case!("Transfer.sol", Transfer, transfer_selfCall, transfer_self, amount: U256);
|
|
|
|
sol!(
|
|
contract MStore8 {
|
|
function mStore8(uint value) public pure returns (uint256 word);
|
|
}
|
|
);
|
|
case!("MStore8.sol", MStore8, mStore8Call, mstore8, value: U256);
|
|
|
|
sol!(
|
|
contract Events {
|
|
event A(uint) anonymous;
|
|
event E(uint indexed, uint indexed, uint indexed);
|
|
|
|
function emitEvent(uint topics) public;
|
|
}
|
|
);
|
|
case!("Events.sol", Events, emitEventCall, event, topics: U256);
|
|
|
|
sol!(
|
|
contract ExtCode {
|
|
function ExtCodeSize(address who) public view returns (uint ret);
|
|
|
|
function CodeSize() public pure returns (uint ret);
|
|
|
|
function ExtCodeHash(address who) public view returns (bytes32 ret);
|
|
|
|
function CodeHash() public view returns (bytes32 ret);
|
|
}
|
|
);
|
|
case!("ExtCode.sol", ExtCode, ExtCodeSizeCall, ext_code_size, address: Address);
|
|
case!("ExtCode.sol", ExtCode, CodeSizeCall, code_size,);
|
|
case!("ExtCode.sol", ExtCode, ExtCodeHashCall, ext_code_hash, address: Address);
|
|
case!("ExtCode.sol", ExtCode, CodeHashCall, code_hash,);
|
|
|
|
sol!(
|
|
contract MCopy {
|
|
function memcpy(bytes memory payload) public pure returns (bytes memory);
|
|
}
|
|
);
|
|
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!(
|
|
contract Call {
|
|
function value_transfer(address payable destination) public payable;
|
|
|
|
function echo(bytes memory payload) public payable returns (bytes memory);
|
|
|
|
function call(
|
|
address callee,
|
|
bytes memory payload
|
|
) public payable returns (bytes memory);
|
|
}
|
|
);
|
|
case!("Call.sol", Call, value_transferCall, call_value_transfer, destination: Address);
|
|
case!("Call.sol", Call, callCall, call_call, destination: Address, payload: Bytes);
|
|
case!("Call.sol", "Call", vec![], call_constructor);
|
|
|
|
sol!(
|
|
contract Value {
|
|
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_selfCall, value_balance_self,);
|
|
|
|
sol!(
|
|
contract Bitwise {
|
|
function opByte(uint i, uint x) public payable returns (uint ret);
|
|
}
|
|
);
|
|
case!("Bitwise.sol", Bitwise, opByteCall, bitwise_byte, index: U256, value: U256);
|
|
|
|
sol!(
|
|
contract Storage {
|
|
function transient(uint value) public returns (uint ret);
|
|
}
|
|
);
|
|
case!("Storage.sol", Storage, transientCall, storage_transient, value: U256);
|
|
|
|
sol!(
|
|
contract Predicted {
|
|
constructor(uint _foo);
|
|
}
|
|
contract AddressPredictor {
|
|
constructor(uint _foo, bytes memory _bytecode) payable;
|
|
}
|
|
);
|
|
case!("AddressPredictor.sol", Predicted, constructorCall, predicted_constructor, salt: U256);
|
|
case!("AddressPredictor.sol", AddressPredictor, constructorCall, address_predictor_constructor, salt: U256, bytecode: Bytes);
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
|
use serde::{de::Deserialize, Serialize};
|
|
use std::{collections::BTreeMap, fs::File};
|
|
|
|
use super::Contract;
|
|
|
|
#[test]
|
|
fn codesize() {
|
|
let path = "codesize.json";
|
|
|
|
let existing = File::open(path)
|
|
.map(|file| {
|
|
BTreeMap::<String, usize>::deserialize(&mut serde_json::Deserializer::from_reader(
|
|
file,
|
|
))
|
|
.expect("should be able to deserialze codesize data")
|
|
})
|
|
.ok();
|
|
|
|
let extract_code_size = |compile: fn() -> Contract| {
|
|
let contract = compile();
|
|
let contract_length = contract.pvm_runtime.len();
|
|
let size_change = existing
|
|
.as_ref()
|
|
.and_then(|map| map.get(contract.name))
|
|
.filter(|old| **old != 0)
|
|
.map(|old| {
|
|
let old = *old as f32;
|
|
let p = (contract_length as f32 - old) / old * 100.0;
|
|
format!("({p}% change from {old} bytes)",)
|
|
})
|
|
.unwrap_or_default();
|
|
|
|
println!("{}: {contract_length} bytes {size_change}", contract.name);
|
|
|
|
(contract.name, contract_length)
|
|
};
|
|
|
|
[
|
|
(|| {
|
|
Contract::build_size_opt(
|
|
vec![],
|
|
"Baseline",
|
|
include_str!("../contracts/Baseline.sol"),
|
|
)
|
|
}) as _,
|
|
(|| {
|
|
Contract::build_size_opt(
|
|
vec![],
|
|
"Flipper",
|
|
include_str!("../contracts/flipper.sol"),
|
|
)
|
|
}) as _,
|
|
(|| {
|
|
Contract::build_size_opt(
|
|
vec![],
|
|
"Computation",
|
|
include_str!("../contracts/Computation.sol"),
|
|
)
|
|
}) as _,
|
|
(|| {
|
|
Contract::build_size_opt(
|
|
vec![],
|
|
"FibonacciIterative",
|
|
include_str!("../contracts/Fibonacci.sol"),
|
|
)
|
|
}) as _,
|
|
(|| Contract::build_size_opt(vec![], "ERC20", include_str!("../contracts/ERC20.sol")))
|
|
as _,
|
|
(|| Contract::build_size_opt(vec![], "SHA1", include_str!("../contracts/SHA1.sol")))
|
|
as _,
|
|
(|| {
|
|
Contract::build_size_opt(
|
|
vec![],
|
|
"DivisionArithmetics",
|
|
include_str!("../contracts/DivisionArithmetics.sol"),
|
|
)
|
|
}) as _,
|
|
(|| Contract::build_size_opt(vec![], "Events", include_str!("../contracts/Events.sol")))
|
|
as _,
|
|
]
|
|
.into_par_iter()
|
|
.map(extract_code_size)
|
|
.collect::<BTreeMap<_, _>>()
|
|
.serialize(&mut serde_json::Serializer::pretty(
|
|
File::create(path).unwrap(),
|
|
))
|
|
.unwrap_or_else(|err| panic!("can not write codesize data to '{path}': {err}"));
|
|
}
|
|
}
|