mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-22 04:27:58 +00:00
Constructors and contract creation (#11)
Implement constructor logic and support create/create2 in the mock runtime Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
Generated
+49
-47
@@ -68,7 +68,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
"syn 2.0.65",
|
||||
"syn-solidity",
|
||||
"tiny-keccak",
|
||||
]
|
||||
@@ -142,9 +142,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.83"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
|
||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||
|
||||
[[package]]
|
||||
name = "ark-ff"
|
||||
@@ -284,7 +284,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -394,9 +394,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.97"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
|
||||
checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@@ -473,7 +473,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -500,9 +500,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "const-hex"
|
||||
version = "1.11.3"
|
||||
version = "1.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ba00838774b4ab0233e355d26710fbfc8327a05c017f6dc4873f876d1f79f78"
|
||||
checksum = "70ff96486ccc291d36a958107caf2c0af8c78c0af7d31ae2f35ce055130de1a6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
@@ -589,9 +589,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
@@ -698,9 +698,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.11.0"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
|
||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
@@ -1029,7 +1029,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "inkwell"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/TheDan64/inkwell.git#6c0fb56b3554e939f9ca61b465043d6a84fb7b95"
|
||||
source = "git+https://github.com/TheDan64/inkwell.git?rev=6c0fb56b3554e939f9ca61b465043d6a84fb7b95#6c0fb56b3554e939f9ca61b465043d6a84fb7b95"
|
||||
dependencies = [
|
||||
"either",
|
||||
"inkwell_internals",
|
||||
@@ -1043,11 +1043,11 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "inkwell_internals"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/TheDan64/inkwell.git#6c0fb56b3554e939f9ca61b465043d6a84fb7b95"
|
||||
source = "git+https://github.com/TheDan64/inkwell.git?rev=6c0fb56b3554e939f9ca61b465043d6a84fb7b95#6c0fb56b3554e939f9ca61b465043d6a84fb7b95"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1141,9 +1141,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.154"
|
||||
version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -1153,9 +1153,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
||||
|
||||
[[package]]
|
||||
name = "libmimalloc-sys"
|
||||
version = "0.1.37"
|
||||
version = "0.1.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81eb4061c0582dedea1cbc7aff2240300dd6982e0239d1c99e65c1dbf4a30ba7"
|
||||
checksum = "0e7bb23d733dfcc8af652a78b7bf232f0e967710d044732185e561e47c0336b6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -1163,9 +1163,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.13"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "lld-sys"
|
||||
@@ -1209,9 +1209,9 @@ checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||
|
||||
[[package]]
|
||||
name = "mimalloc"
|
||||
version = "0.1.41"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f41a2280ded0da56c8cf898babb86e8f10651a34adcfff190ae9a1159c6908d"
|
||||
checksum = "e9186d86b79b52f4a77af65604b51225e8db1d6ee7e3f41aec1e40829c71a176"
|
||||
dependencies = [
|
||||
"libmimalloc-sys",
|
||||
]
|
||||
@@ -1315,6 +1315,7 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
name = "pallet-contracts-pvm-llapi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"inkwell",
|
||||
]
|
||||
|
||||
@@ -1379,9 +1380,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
|
||||
checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"plotters-backend",
|
||||
@@ -1392,15 +1393,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "plotters-backend"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
|
||||
checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7"
|
||||
|
||||
[[package]]
|
||||
name = "plotters-svg"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
|
||||
checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705"
|
||||
dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
@@ -1518,9 +1519,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.82"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
|
||||
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1732,6 +1733,7 @@ dependencies = [
|
||||
"alloy-sol-types",
|
||||
"env_logger",
|
||||
"hex",
|
||||
"log",
|
||||
"polkavm",
|
||||
"rayon",
|
||||
"revive-common",
|
||||
@@ -2026,7 +2028,7 @@ checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2198,9 +2200,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.63"
|
||||
version = "2.0.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704"
|
||||
checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2216,7 +2218,7 @@ dependencies = [
|
||||
"paste",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2248,22 +2250,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.60"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
|
||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.60"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
|
||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2287,9 +2289,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.5"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||
checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
@@ -2414,7 +2416,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
"syn 2.0.65",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -2436,7 +2438,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
"syn 2.0.65",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -2675,7 +2677,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2695,7 +2697,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+2
-1
@@ -36,6 +36,7 @@ alloy-sol-types = "0.6"
|
||||
env_logger = { version = "0.10.0", default-features = false }
|
||||
serde_stacker = "0.1"
|
||||
criterion = { version = "0.5", features = ["html_reports"] }
|
||||
log = { version = "0.4" }
|
||||
|
||||
# Benchmarking against EVM
|
||||
primitive-types = { version = "0.12", features = ["codec"] }
|
||||
@@ -43,7 +44,7 @@ evm-interpreter = { git = "https://github.com/xermicus/evm.git", branch = "separ
|
||||
|
||||
[workspace.dependencies.inkwell]
|
||||
git = "https://github.com/TheDan64/inkwell.git"
|
||||
commit = "d916c66"
|
||||
rev = "6c0fb56b3554e939f9ca61b465043d6a84fb7b95"
|
||||
default-features = false
|
||||
features = ["serde", "llvm18-0", "no-libffi-linking", "target-riscv"]
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ The project is in a very early PoC phase. Don't yet expect the produced code to
|
||||
- [x] Use PolkaVM allocator for heap space
|
||||
- [ ] Exercice `schlau` and possibly `smart-bench` benchmark cases
|
||||
- [x] Tests currently rely on the binary being in $PATH, which is very annoying and requires `cargo install` all the times
|
||||
- [ ] Define how to do deployments
|
||||
- [x] Define how to do deployments
|
||||
- [ ] Calling conventions for calling other contracts
|
||||
- [ ] Runtime environment isn't fully figured out; implement all EVM builtins
|
||||
- [ ] Iron out many leftovers from the ZKVM target
|
||||
@@ -36,3 +36,4 @@ The project is in a very early PoC phase. Don't yet expect the produced code to
|
||||
- [ ] Document differences from EVM
|
||||
- [ ] Audit for bugs and correctness
|
||||
- [x] Rebranding
|
||||
- [ ] Remove support for vyper
|
||||
|
||||
@@ -11,6 +11,7 @@ alloy-primitives = { workspace = true }
|
||||
alloy-sol-types = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
env_logger = { workspace = true }
|
||||
log = { workspace = true }
|
||||
|
||||
revive-solidity = { path = "../solidity" }
|
||||
revive-differential = { path = "../differential" }
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"Baseline": 2824,
|
||||
"Computation": 6253,
|
||||
"DivisionArithmetics": 41341,
|
||||
"ERC20": 54329,
|
||||
"Events": 3642,
|
||||
"FibonacciIterative": 4866,
|
||||
"Flipper": 3270,
|
||||
"SHA1": 34605
|
||||
"Baseline": 934,
|
||||
"Computation": 4360,
|
||||
"DivisionArithmetics": 39448,
|
||||
"ERC20": 52461,
|
||||
"Events": 1749,
|
||||
"FibonacciIterative": 2973,
|
||||
"Flipper": 3368,
|
||||
"SHA1": 32709
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.24;
|
||||
|
||||
contract CreateA {
|
||||
address creator;
|
||||
|
||||
constructor() payable {
|
||||
creator = msg.sender;
|
||||
}
|
||||
}
|
||||
|
||||
contract CreateB {
|
||||
receive() external payable {
|
||||
new CreateA{value: msg.value}();
|
||||
}
|
||||
|
||||
fallback() external {
|
||||
new CreateA{salt: hex"01"}();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,10 @@ pragma solidity ^0.8;
|
||||
contract Flipper {
|
||||
bool coin;
|
||||
|
||||
constructor(bool _coin) {
|
||||
coin = _coin;
|
||||
}
|
||||
|
||||
function flip() public {
|
||||
coin = !coin;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use alloy_primitives::{I256, U256};
|
||||
use alloy_sol_types::{sol, SolCall};
|
||||
use alloy_sol_types::{sol, SolCall, SolConstructor};
|
||||
|
||||
use crate::mock_runtime::{CallOutput, State};
|
||||
|
||||
@@ -13,7 +13,11 @@ pub struct Contract {
|
||||
|
||||
sol!(contract Baseline { function baseline() public payable; });
|
||||
|
||||
sol!(contract Flipper { function flip() public; });
|
||||
sol!(contract Flipper {
|
||||
constructor (bool);
|
||||
|
||||
function flip() public;
|
||||
});
|
||||
|
||||
sol!(contract Computation {
|
||||
function odd_product(int32 n) public pure returns (int64);
|
||||
@@ -113,6 +117,12 @@ sol!(
|
||||
}
|
||||
);
|
||||
|
||||
sol!(
|
||||
contract CreateB {
|
||||
fallback() external payable;
|
||||
}
|
||||
);
|
||||
|
||||
impl Contract {
|
||||
/// Execute the contract.
|
||||
///
|
||||
@@ -228,6 +238,18 @@ impl Contract {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flipper_constructor(coin: bool) -> Self {
|
||||
let code = include_str!("../contracts/flipper.sol");
|
||||
let name = "Flipper";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Flipper::constructorCall::new((coin,)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn erc20() -> Self {
|
||||
let code = include_str!("../contracts/ERC20.sol");
|
||||
let name = "ERC20";
|
||||
@@ -359,6 +381,30 @@ impl Contract {
|
||||
calldata: Events::emitEventCall::new((topics,)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_a() -> Self {
|
||||
let code = include_str!("../contracts/Create.sol");
|
||||
let name = "CreateA";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: vec![0; 4],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_b() -> Self {
|
||||
let code = include_str!("../contracts/Create.sol");
|
||||
let name = "CreateB";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: vec![0; 4],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -10,10 +10,10 @@ use revive_llvm_context::polkavm_const::runtime_api;
|
||||
|
||||
/// The mocked blockchain account.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
struct Account {
|
||||
value: U256,
|
||||
contract: Option<U256>,
|
||||
storage: HashMap<U256, U256>,
|
||||
pub struct Account {
|
||||
pub value: U256,
|
||||
pub contract: Option<B256>,
|
||||
pub storage: HashMap<U256, U256>,
|
||||
}
|
||||
|
||||
/// Emitted event data.
|
||||
@@ -40,7 +40,7 @@ pub struct CallOutput {
|
||||
enum Export {
|
||||
#[default]
|
||||
Call,
|
||||
Deploy(U256),
|
||||
Deploy(B256),
|
||||
}
|
||||
|
||||
/// Possible contract call return flags.
|
||||
@@ -107,6 +107,8 @@ pub struct Transaction {
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
pub const CALL_STACK_SIZE: usize = 1024;
|
||||
|
||||
pub fn default_address() -> Address {
|
||||
Address::default().create2(B256::default(), keccak256([]).0)
|
||||
}
|
||||
@@ -129,10 +131,8 @@ impl Transaction {
|
||||
.unwrap_or_else(|| panic!("callee has no associated account: {account}"))
|
||||
}
|
||||
|
||||
fn create2(&self, salt: U256, blob_hash: U256) -> Address {
|
||||
self.top_frame()
|
||||
.callee
|
||||
.create2(salt.to_be_bytes(), blob_hash.to_be_bytes())
|
||||
fn create2(&self, salt: B256, blob_hash: B256) -> Address {
|
||||
self.top_frame().callee.create2(salt, blob_hash)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,10 +168,26 @@ impl TransactionBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the transaction to deploy code.
|
||||
pub fn deploy(mut self, code: &[u8]) -> Self {
|
||||
/// Helper to setup the transaction for deploy code.
|
||||
/// - Simulate an upload of the `code`
|
||||
/// - Set the export to `deploy`
|
||||
/// - Derive address based on the caller and `salt` value
|
||||
/// - Set the callee to the derived address
|
||||
/// - Create a new default account at the derived address
|
||||
pub fn deploy(mut self, code: &[u8], salt: Option<B256>) -> Self {
|
||||
let blob_hash = self.context.state.upload_code(code);
|
||||
let address = self
|
||||
.context
|
||||
.top_frame()
|
||||
.caller
|
||||
.create2(salt.unwrap_or_default(), blob_hash);
|
||||
|
||||
self.context.top_frame_mut().export = Export::Deploy(blob_hash);
|
||||
self.context.top_frame_mut().callee = address;
|
||||
self.context
|
||||
.state
|
||||
.create_account(address, Default::default(), blob_hash);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
@@ -183,7 +199,7 @@ impl TransactionBuilder {
|
||||
self.context.state.create_account(
|
||||
Transaction::default_address(),
|
||||
Default::default(),
|
||||
keccak256(code).into(),
|
||||
keccak256(code),
|
||||
);
|
||||
self
|
||||
}
|
||||
@@ -193,12 +209,11 @@ impl TransactionBuilder {
|
||||
/// Reverts any state changes if the contract reverts or the exuection traps.
|
||||
pub fn call(mut self) -> (State, CallOutput) {
|
||||
let blob_hash = match self.context.top_frame().export {
|
||||
Export::Call => self.context.top_account_mut().contract.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"not a contract account: {}",
|
||||
self.context.top_frame().callee
|
||||
)
|
||||
}),
|
||||
Export::Call => self
|
||||
.context
|
||||
.top_account_mut()
|
||||
.contract
|
||||
.expect("balance transfer"),
|
||||
Export::Deploy(blob_hash) => blob_hash,
|
||||
};
|
||||
let code = self
|
||||
@@ -207,7 +222,12 @@ impl TransactionBuilder {
|
||||
.blobs
|
||||
.get(&blob_hash)
|
||||
.unwrap_or_else(|| panic!("contract code not found: {blob_hash}"));
|
||||
let (mut instance, export) = prepare(code, None);
|
||||
let (mut instance, _) = prepare(code, None);
|
||||
let export = match self.context.top_frame().export {
|
||||
Export::Call => runtime_api::CALL,
|
||||
Export::Deploy(_) => runtime_api::DEPLOY,
|
||||
};
|
||||
let export = instance.module().lookup_export(export).unwrap();
|
||||
self.call_on(&mut instance, export)
|
||||
}
|
||||
|
||||
@@ -223,14 +243,6 @@ impl TransactionBuilder {
|
||||
let mut state_args = polkavm::StateArgs::default();
|
||||
state_args.set_gas(polkavm::Gas::MAX);
|
||||
|
||||
if let Export::Deploy(blob_hash) = self.context.top_frame().export {
|
||||
self.context.state.create_account(
|
||||
self.context.create2(Default::default(), blob_hash),
|
||||
self.context.top_frame().callvalue,
|
||||
blob_hash,
|
||||
);
|
||||
}
|
||||
|
||||
let callvalue = self.context.top_frame().callvalue;
|
||||
self.context.top_account_mut().value += callvalue;
|
||||
|
||||
@@ -271,7 +283,7 @@ impl From<State> for TransactionBuilder {
|
||||
/// The mocked blockchain state.
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct State {
|
||||
blobs: HashMap<U256, Vec<u8>>,
|
||||
blobs: HashMap<B256, Vec<u8>>,
|
||||
accounts: HashMap<Address, Account>,
|
||||
}
|
||||
|
||||
@@ -279,6 +291,19 @@ impl State {
|
||||
pub const BLOCK_NUMBER: u64 = 123;
|
||||
pub const BLOCK_TIMESTAMP: u64 = 456;
|
||||
|
||||
pub fn new_deployed(contract: crate::Contract) -> (Self, Address) {
|
||||
let (state, output) = State::default()
|
||||
.transaction()
|
||||
.deploy(&contract.pvm_runtime, None)
|
||||
.calldata(contract.calldata)
|
||||
.call();
|
||||
assert_eq!(output.flags, ReturnFlags::Success);
|
||||
|
||||
let address = *state.accounts().keys().next().unwrap();
|
||||
|
||||
(state, address)
|
||||
}
|
||||
|
||||
pub fn transaction(self) -> TransactionBuilder {
|
||||
TransactionBuilder {
|
||||
state_before: self.clone(),
|
||||
@@ -289,8 +314,8 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn upload_code(&mut self, code: &[u8]) -> U256 {
|
||||
let blob_hash = keccak256(code).into();
|
||||
pub fn upload_code(&mut self, code: &[u8]) -> B256 {
|
||||
let blob_hash = keccak256(code);
|
||||
self.blobs.insert(blob_hash, code.to_vec());
|
||||
blob_hash
|
||||
}
|
||||
@@ -308,7 +333,7 @@ impl State {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn create_account(&mut self, address: Address, value: U256, blob_hash: U256) {
|
||||
pub fn create_account(&mut self, address: Address, value: U256, blob_hash: B256) {
|
||||
self.accounts.insert(
|
||||
address,
|
||||
Account {
|
||||
@@ -318,6 +343,10 @@ impl State {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn accounts(&self) -> &HashMap<Address, Account> {
|
||||
&self.accounts
|
||||
}
|
||||
}
|
||||
|
||||
fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
|
||||
@@ -608,6 +637,99 @@ fn link_host_functions(engine: &Engine) -> Linker<Transaction> {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
linker
|
||||
.func_wrap(
|
||||
runtime_api::INSTANTIATE,
|
||||
|caller: Caller<Transaction>, argument_ptr: u32| {
|
||||
let (mut caller, transaction) = caller.split();
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(packed)]
|
||||
struct Arguments {
|
||||
code_hash_ptr: u32,
|
||||
ref_time_limit: u64,
|
||||
proof_size_limit: u64,
|
||||
deposit_ptr: u32,
|
||||
value_ptr: u32,
|
||||
input_data_ptr: u32,
|
||||
input_data_len: u32,
|
||||
address_ptr: u32,
|
||||
address_len_ptr: u32,
|
||||
output_ptr: u32,
|
||||
output_len_ptr: u32,
|
||||
salt_ptr: u32,
|
||||
salt_len: u32,
|
||||
}
|
||||
let mut buffer = [0; std::mem::size_of::<Arguments>()];
|
||||
caller.read_memory_into_slice(argument_ptr, &mut buffer)?;
|
||||
let arguments: Arguments = unsafe { std::mem::transmute(buffer) };
|
||||
|
||||
assert_eq!({ arguments.ref_time_limit }, 0);
|
||||
assert_eq!({ arguments.proof_size_limit }, 0);
|
||||
assert_eq!({ arguments.deposit_ptr }, u32::MAX);
|
||||
assert_eq!({ arguments.output_ptr }, u32::MAX);
|
||||
assert_eq!({ arguments.output_len_ptr }, u32::MAX);
|
||||
assert_eq!({ arguments.salt_len }, 32);
|
||||
|
||||
if transaction.stack.len() >= Transaction::CALL_STACK_SIZE {
|
||||
log::info!("deployment faild: maximum stack depth reached");
|
||||
caller.write_memory(arguments.address_ptr, &Address::ZERO.0 .0)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let blob_hash = caller.read_memory_into_vec(arguments.code_hash_ptr, 32)?;
|
||||
let blob_hash = B256::from_slice(&blob_hash);
|
||||
let value = caller.read_memory_into_vec(arguments.value_ptr, 20)?;
|
||||
let input_data = caller
|
||||
.read_memory_into_vec(arguments.input_data_ptr, arguments.input_data_len)?;
|
||||
|
||||
let address_len = caller.read_u32(arguments.address_len_ptr)?;
|
||||
assert_eq!(address_len, 20);
|
||||
|
||||
let salt = caller.read_memory_into_vec(arguments.salt_ptr, arguments.salt_len)?;
|
||||
let salt = B256::from_slice(&salt);
|
||||
let address = transaction.create2(salt, blob_hash);
|
||||
if transaction.state.accounts.contains_key(&address) {
|
||||
log::info!("deployment failed: address {address} already exists");
|
||||
caller.write_memory(arguments.address_ptr, &Address::ZERO.0 .0)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let amount = U256::from_le_slice(&value);
|
||||
match transaction.top_account_mut().value.checked_sub(amount) {
|
||||
Some(deducted) => transaction.top_account_mut().value = deducted,
|
||||
None => {
|
||||
log::info!("deployment failed: insufficient balance {amount}");
|
||||
caller.write_memory(arguments.address_ptr, &Address::ZERO.0 .0)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let (state, output) = transaction
|
||||
.state
|
||||
.clone()
|
||||
.transaction()
|
||||
.callee(address)
|
||||
.deploy(transaction.state.blobs.get(&blob_hash).unwrap(), Some(salt))
|
||||
.callvalue(amount)
|
||||
.calldata(input_data)
|
||||
.call();
|
||||
|
||||
let result = if output.flags == ReturnFlags::Success {
|
||||
log::info!("deployment succeeded");
|
||||
transaction.state = state;
|
||||
address
|
||||
} else {
|
||||
log::info!("deployment failed: callee reverted {:?}", output.flags);
|
||||
Address::ZERO
|
||||
};
|
||||
caller.write_memory(arguments.address_ptr, &result.0 .0)?;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
linker
|
||||
}
|
||||
|
||||
@@ -634,7 +756,8 @@ pub fn instantiate_module(
|
||||
}
|
||||
|
||||
pub fn prepare(code: &[u8], config: Option<Config>) -> (Instance<Transaction>, ExportIndex) {
|
||||
let blob = ProgramBlob::parse(code.into()).unwrap();
|
||||
let blob = ProgramBlob::parse(code.into())
|
||||
.unwrap_or_else(|err| panic!("{err}\n{}", hex::encode(code)));
|
||||
|
||||
let engine = Engine::new(&config.unwrap_or_default()).unwrap();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use alloy_primitives::{keccak256, Address, FixedBytes, I256, U256};
|
||||
use alloy_primitives::{keccak256, Address, FixedBytes, B256, I256, U256};
|
||||
use alloy_sol_types::{sol, SolCall};
|
||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||
use sha1::Digest;
|
||||
@@ -27,16 +27,24 @@ fn fibonacci() {
|
||||
|
||||
#[test]
|
||||
fn flipper() {
|
||||
let (state, address) = State::new_deployed(Contract::flipper_constructor(true));
|
||||
|
||||
let contract = Contract::flipper();
|
||||
let address = Transaction::default_address();
|
||||
|
||||
let (state, output) = contract.execute();
|
||||
assert_eq!(output.flags, ReturnFlags::Success);
|
||||
state.assert_storage_key(address, U256::ZERO, U256::from(1));
|
||||
|
||||
let (state, output) = state.transaction().calldata(contract.calldata).call();
|
||||
let (state, output) = state
|
||||
.transaction()
|
||||
.calldata(contract.calldata.clone())
|
||||
.callee(address)
|
||||
.call();
|
||||
assert_eq!(output.flags, ReturnFlags::Success);
|
||||
state.assert_storage_key(address, U256::ZERO, U256::ZERO);
|
||||
|
||||
let (state, output) = state
|
||||
.transaction()
|
||||
.calldata(contract.calldata)
|
||||
.callee(address)
|
||||
.call();
|
||||
assert_eq!(output.flags, ReturnFlags::Success);
|
||||
state.assert_storage_key(address, U256::ZERO, U256::from(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -421,3 +429,81 @@ fn events() {
|
||||
assert_success(&Contract::event(U256::ZERO), true);
|
||||
assert_success(&Contract::event(U256::from(123)), true);
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,9 +33,6 @@ pub static GLOBAL_CALL_FLAGS: &str = "call_flags";
|
||||
/// The extra ABI data global variable name.
|
||||
pub static GLOBAL_EXTRA_ABI_DATA: &str = "extra_abi_data";
|
||||
|
||||
/// The active pointer global variable name.
|
||||
pub static GLOBAL_ACTIVE_POINTER: &str = "ptr_active";
|
||||
|
||||
/// The constant array global variable name prefix.
|
||||
pub static GLOBAL_CONST_ARRAY_PREFIX: &str = "const_array_";
|
||||
|
||||
@@ -74,10 +71,5 @@ pub const NO_SYSTEM_CALL_BIT: bool = false;
|
||||
pub const SYSTEM_CALL_BIT: bool = true;
|
||||
|
||||
/// The deployer call header size that consists of:
|
||||
/// - selector (4 bytes)
|
||||
/// - salt (32 bytes)
|
||||
/// - bytecode hash (32 bytes)
|
||||
/// - constructor arguments offset (32 bytes)
|
||||
/// - constructor arguments length (32 bytes)
|
||||
pub const DEPLOYER_CALL_HEADER_SIZE: usize =
|
||||
revive_common::BYTE_LENGTH_X32 + (revive_common::BYTE_LENGTH_WORD * 4);
|
||||
pub const DEPLOYER_CALL_HEADER_SIZE: usize = revive_common::BYTE_LENGTH_WORD;
|
||||
|
||||
@@ -24,6 +24,8 @@ pub static HASH_KECCAK_256: &str = "hash_keccak_256";
|
||||
|
||||
pub static INPUT: &str = "input";
|
||||
|
||||
pub static INSTANTIATE: &str = "instantiate";
|
||||
|
||||
pub static NOW: &str = "now";
|
||||
|
||||
pub static RETURN: &str = "seal_return";
|
||||
|
||||
@@ -128,7 +128,7 @@ impl<'ctx> Function<'ctx> {
|
||||
) {
|
||||
for attribute_kind in attributes.into_iter() {
|
||||
match attribute_kind {
|
||||
Attribute::Memory => todo!("`memory` attributes are not yet implemented"),
|
||||
Attribute::Memory => unimplemented!("`memory` attributes are not implemented"),
|
||||
attribute_kind @ Attribute::AlwaysInline if force => {
|
||||
let is_optimize_none_set = declaration
|
||||
.value
|
||||
|
||||
@@ -1,324 +0,0 @@
|
||||
//! The `deployer_call` function.
|
||||
|
||||
use inkwell::types::BasicType;
|
||||
|
||||
use crate::polkavm::context::address_space::AddressSpace;
|
||||
use crate::polkavm::context::function::Function;
|
||||
use crate::polkavm::context::pointer::Pointer;
|
||||
use crate::polkavm::context::Context;
|
||||
use crate::polkavm::Dependency;
|
||||
use crate::polkavm::WriteLLVM;
|
||||
|
||||
/// The `deployer_call` function.
|
||||
/// Calls the deployer system contract, which returns the newly deployed contract address or 0.
|
||||
/// The address is returned in the first 32-byte word of the return data. If it is 0, the 0 is
|
||||
/// returned. If the entire call has failed, there is also a 0 returned.
|
||||
#[derive(Debug)]
|
||||
pub struct DeployerCall {
|
||||
/// The address space where the calldata is allocated.
|
||||
/// Solidity uses the ordinary heap. Vyper uses the auxiliary heap.
|
||||
address_space: AddressSpace,
|
||||
}
|
||||
|
||||
impl DeployerCall {
|
||||
/// The default function name.
|
||||
pub const FUNCTION_NAME: &'static str = "__deployer_call";
|
||||
|
||||
/// The value argument index.
|
||||
pub const ARGUMENT_INDEX_VALUE: usize = 0;
|
||||
|
||||
/// The input offset argument index.
|
||||
pub const ARGUMENT_INDEX_INPUT_OFFSET: usize = 1;
|
||||
|
||||
/// The input length argument index.
|
||||
pub const ARGUMENT_INDEX_INPUT_LENGTH: usize = 2;
|
||||
|
||||
/// The signature hash argument index.
|
||||
pub const ARGUMENT_INDEX_SIGNATURE_HASH: usize = 3;
|
||||
|
||||
/// The salt argument index.
|
||||
pub const ARGUMENT_INDEX_SALT: usize = 4;
|
||||
|
||||
/// A shortcut constructor.
|
||||
pub fn new(address_space: AddressSpace) -> Self {
|
||||
Self { address_space }
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> WriteLLVM<D> for DeployerCall
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||
let function_type = context.function_type(
|
||||
vec![
|
||||
context.word_type().as_basic_type_enum(),
|
||||
context.word_type().as_basic_type_enum(),
|
||||
context.word_type().as_basic_type_enum(),
|
||||
context.word_type().as_basic_type_enum(),
|
||||
context.word_type().as_basic_type_enum(),
|
||||
],
|
||||
1,
|
||||
false,
|
||||
);
|
||||
let function = context.add_function(
|
||||
Self::FUNCTION_NAME,
|
||||
function_type,
|
||||
1,
|
||||
Some(inkwell::module::Linkage::External),
|
||||
)?;
|
||||
Function::set_frontend_runtime_attributes(
|
||||
context.llvm,
|
||||
function.borrow().declaration(),
|
||||
&context.optimizer,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
|
||||
context.set_current_function(Self::FUNCTION_NAME)?;
|
||||
|
||||
let value = context
|
||||
.current_function()
|
||||
.borrow()
|
||||
.get_nth_param(Self::ARGUMENT_INDEX_VALUE)
|
||||
.into_int_value();
|
||||
let input_offset = context
|
||||
.current_function()
|
||||
.borrow()
|
||||
.get_nth_param(Self::ARGUMENT_INDEX_INPUT_OFFSET)
|
||||
.into_int_value();
|
||||
let input_length = context
|
||||
.current_function()
|
||||
.borrow()
|
||||
.get_nth_param(Self::ARGUMENT_INDEX_INPUT_LENGTH)
|
||||
.into_int_value();
|
||||
let signature_hash = context
|
||||
.current_function()
|
||||
.borrow()
|
||||
.get_nth_param(Self::ARGUMENT_INDEX_SIGNATURE_HASH)
|
||||
.into_int_value();
|
||||
let salt = context
|
||||
.current_function()
|
||||
.borrow()
|
||||
.get_nth_param(Self::ARGUMENT_INDEX_SALT)
|
||||
.into_int_value();
|
||||
|
||||
let error_block = context.append_basic_block("deployer_call_error_block");
|
||||
let success_block = context.append_basic_block("deployer_call_success_block");
|
||||
let value_zero_block = context.append_basic_block("deployer_call_value_zero_block");
|
||||
let value_non_zero_block = context.append_basic_block("deployer_call_value_non_zero_block");
|
||||
let value_join_block = context.append_basic_block("deployer_call_value_join_block");
|
||||
|
||||
context.set_basic_block(context.current_function().borrow().entry_block());
|
||||
let _abi_data = crate::polkavm::utils::abi_data(
|
||||
context,
|
||||
input_offset,
|
||||
input_length,
|
||||
None,
|
||||
self.address_space,
|
||||
true,
|
||||
)?;
|
||||
|
||||
let signature_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
self.address_space,
|
||||
context.word_type(),
|
||||
input_offset,
|
||||
"deployer_call_signature_pointer",
|
||||
);
|
||||
context.build_store(signature_pointer, signature_hash)?;
|
||||
|
||||
let salt_offset = context.builder().build_int_add(
|
||||
input_offset,
|
||||
context.word_const(revive_common::BYTE_LENGTH_X32 as u64),
|
||||
"deployer_call_salt_offset",
|
||||
)?;
|
||||
let salt_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
self.address_space,
|
||||
context.word_type(),
|
||||
salt_offset,
|
||||
"deployer_call_salt_pointer",
|
||||
);
|
||||
context.build_store(salt_pointer, salt)?;
|
||||
|
||||
let arguments_offset_offset = context.builder().build_int_add(
|
||||
salt_offset,
|
||||
context.word_const((revive_common::BYTE_LENGTH_WORD * 2) as u64),
|
||||
"deployer_call_arguments_offset_offset",
|
||||
)?;
|
||||
let arguments_offset_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
self.address_space,
|
||||
context.word_type(),
|
||||
arguments_offset_offset,
|
||||
"deployer_call_arguments_offset_pointer",
|
||||
);
|
||||
context.build_store(
|
||||
arguments_offset_pointer,
|
||||
context.word_const(
|
||||
(crate::polkavm::DEPLOYER_CALL_HEADER_SIZE
|
||||
- (revive_common::BYTE_LENGTH_X32 + revive_common::BYTE_LENGTH_WORD))
|
||||
as u64,
|
||||
),
|
||||
)?;
|
||||
|
||||
let arguments_length_offset = context.builder().build_int_add(
|
||||
arguments_offset_offset,
|
||||
context.word_const(revive_common::BYTE_LENGTH_WORD as u64),
|
||||
"deployer_call_arguments_length_offset",
|
||||
)?;
|
||||
let arguments_length_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
self.address_space,
|
||||
context.word_type(),
|
||||
arguments_length_offset,
|
||||
"deployer_call_arguments_length_pointer",
|
||||
);
|
||||
let arguments_length_value = context.builder().build_int_sub(
|
||||
input_length,
|
||||
context.word_const(crate::polkavm::DEPLOYER_CALL_HEADER_SIZE as u64),
|
||||
"deployer_call_arguments_length",
|
||||
)?;
|
||||
context.build_store(arguments_length_pointer, arguments_length_value)?;
|
||||
|
||||
let result_pointer =
|
||||
context.build_alloca(context.word_type(), "deployer_call_result_pointer");
|
||||
context.build_store(result_pointer, context.word_const(0))?;
|
||||
let deployer_call_result_type = context.structure_type(&[
|
||||
context
|
||||
.llvm()
|
||||
.ptr_type(AddressSpace::Generic.into())
|
||||
.as_basic_type_enum(),
|
||||
context.bool_type().as_basic_type_enum(),
|
||||
]);
|
||||
let deployer_call_result_pointer =
|
||||
context.build_alloca(deployer_call_result_type, "deployer_call_result_pointer");
|
||||
context.build_store(
|
||||
deployer_call_result_pointer,
|
||||
deployer_call_result_type.const_zero(),
|
||||
)?;
|
||||
let is_value_zero = context.builder().build_int_compare(
|
||||
inkwell::IntPredicate::EQ,
|
||||
value,
|
||||
context.word_const(0),
|
||||
"deployer_call_is_value_zero",
|
||||
)?;
|
||||
context.build_conditional_branch(is_value_zero, value_zero_block, value_non_zero_block)?;
|
||||
|
||||
context.set_basic_block(value_zero_block);
|
||||
//let deployer_call_result = context
|
||||
// .build_call(
|
||||
// context.llvm_runtime().far_call,
|
||||
// crate::polkavm::utils::external_call_arguments(
|
||||
// context,
|
||||
// abi_data,
|
||||
// context.field_const(zkevm_opcode_defs::ADDRESS_CONTRACT_DEPLOYER.into()),
|
||||
// vec![],
|
||||
// None,
|
||||
// )
|
||||
// .as_slice(),
|
||||
// "deployer_call_ordinary",
|
||||
// )
|
||||
// .expect("Always returns a value");
|
||||
//context.build_store(deployer_call_result_pointer, deployer_call_result)?;
|
||||
context.build_unconditional_branch(value_join_block);
|
||||
|
||||
context.set_basic_block(value_non_zero_block);
|
||||
//let deployer_call_result = context
|
||||
// .build_call(
|
||||
// context.llvm_runtime().far_call,
|
||||
// crate::polkavm::utils::external_call_arguments(
|
||||
// context,
|
||||
// abi_data.as_basic_value_enum(),
|
||||
// context.field_const(zkevm_opcode_defs::ADDRESS_MSG_VALUE.into()),
|
||||
// vec![
|
||||
// value,
|
||||
// context.field_const(zkevm_opcode_defs::ADDRESS_CONTRACT_DEPLOYER.into()),
|
||||
// context.field_const(u64::from(crate::polkavm::r#const::SYSTEM_CALL_BIT)),
|
||||
// ],
|
||||
// None,
|
||||
// )
|
||||
// .as_slice(),
|
||||
// "deployer_call_system",
|
||||
// )
|
||||
// .expect("Always returns a value");
|
||||
//context.build_store(deployer_call_result_pointer, deployer_call_result)?;
|
||||
context.build_unconditional_branch(value_join_block);
|
||||
|
||||
context.set_basic_block(value_join_block);
|
||||
let result_abi_data_pointer = context.build_gep(
|
||||
deployer_call_result_pointer,
|
||||
&[
|
||||
context.word_const(0),
|
||||
context
|
||||
.integer_type(revive_common::BIT_LENGTH_X32)
|
||||
.const_zero(),
|
||||
],
|
||||
context
|
||||
.llvm()
|
||||
.ptr_type(AddressSpace::Generic.into())
|
||||
.as_basic_type_enum(),
|
||||
"deployer_call_result_abi_data_pointer",
|
||||
);
|
||||
let result_abi_data =
|
||||
context.build_load(result_abi_data_pointer, "deployer_call_result_abi_data")?;
|
||||
|
||||
let result_status_code_pointer = context.build_gep(
|
||||
deployer_call_result_pointer,
|
||||
&[
|
||||
context.word_const(0),
|
||||
context
|
||||
.integer_type(revive_common::BIT_LENGTH_X32)
|
||||
.const_int(1, false),
|
||||
],
|
||||
context.bool_type().as_basic_type_enum(),
|
||||
"contract_call_external_result_status_code_pointer",
|
||||
);
|
||||
let result_status_code_boolean = context
|
||||
.build_load(
|
||||
result_status_code_pointer,
|
||||
"contract_call_external_result_status_code_boolean",
|
||||
)?
|
||||
.into_int_value();
|
||||
|
||||
context.build_conditional_branch(result_status_code_boolean, success_block, error_block)?;
|
||||
|
||||
context.set_basic_block(success_block);
|
||||
let result_abi_data_pointer = Pointer::new(
|
||||
context.word_type(),
|
||||
AddressSpace::Generic,
|
||||
result_abi_data.into_pointer_value(),
|
||||
);
|
||||
let address_or_status_code = context.build_load(
|
||||
result_abi_data_pointer,
|
||||
"deployer_call_address_or_status_code",
|
||||
)?;
|
||||
context.build_store(result_pointer, address_or_status_code)?;
|
||||
context.build_unconditional_branch(context.current_function().borrow().return_block());
|
||||
|
||||
context.set_basic_block(error_block);
|
||||
let result_abi_data_pointer = Pointer::new(
|
||||
context.byte_type(),
|
||||
AddressSpace::Generic,
|
||||
result_abi_data.into_pointer_value(),
|
||||
);
|
||||
context.write_abi_pointer(
|
||||
result_abi_data_pointer,
|
||||
crate::polkavm::GLOBAL_RETURN_DATA_POINTER,
|
||||
);
|
||||
context.write_abi_data_size(
|
||||
result_abi_data_pointer,
|
||||
crate::polkavm::GLOBAL_RETURN_DATA_SIZE,
|
||||
);
|
||||
context.build_unconditional_branch(context.current_function().borrow().return_block());
|
||||
|
||||
context.set_basic_block(context.current_function().borrow().return_block());
|
||||
let result = context.build_load(result_pointer, "deployer_call_result")?;
|
||||
context.build_return(Some(&result));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
//! The entry function.
|
||||
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::polkavm::context::address_space::AddressSpace;
|
||||
use crate::polkavm::context::function::runtime::Runtime;
|
||||
@@ -8,7 +9,6 @@ use crate::polkavm::context::Context;
|
||||
use crate::polkavm::r#const::*;
|
||||
use crate::polkavm::Dependency;
|
||||
use crate::polkavm::WriteLLVM;
|
||||
use crate::PolkaVMPointer as Pointer;
|
||||
|
||||
/// The entry function.
|
||||
/// The function is a wrapper managing the runtime and deploy code calling logic.
|
||||
@@ -23,8 +23,11 @@ impl Entry {
|
||||
/// The number of mandatory arguments.
|
||||
pub const MANDATORY_ARGUMENTS_COUNT: usize = 2;
|
||||
|
||||
/// Reserve 1kb for calldata.
|
||||
pub const MAX_CALLDATA_SIZE: usize = 1024;
|
||||
/// Reserve 1mb for calldata.
|
||||
pub const MAX_CALLDATA_SIZE: usize = 1024 * 1024;
|
||||
|
||||
/// Reserve 1mb for returndata.
|
||||
pub const MAX_RETURNDATA_SIZE: usize = 1024 * 1024;
|
||||
|
||||
/// Initializes the global variables.
|
||||
/// The pointers are not initialized, because it's not possible to create a null pointer.
|
||||
@@ -40,6 +43,14 @@ impl Entry {
|
||||
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(
|
||||
crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER,
|
||||
context.llvm().ptr_type(AddressSpace::Generic.into()),
|
||||
@@ -61,9 +72,9 @@ impl Entry {
|
||||
);
|
||||
context.set_global(
|
||||
crate::polkavm::GLOBAL_RETURN_DATA_SIZE,
|
||||
context.word_type(),
|
||||
context.xlen_type(),
|
||||
AddressSpace::Stack,
|
||||
context.word_const(0),
|
||||
context.xlen_type().const_zero().as_basic_value_enum(),
|
||||
);
|
||||
|
||||
context.set_global(
|
||||
@@ -162,27 +173,6 @@ impl Entry {
|
||||
calldata_size_casted,
|
||||
);
|
||||
|
||||
// Store calldata end pointer
|
||||
let input_pointer = Pointer::new(
|
||||
input_pointer.get_type(),
|
||||
AddressSpace::Generic,
|
||||
input_pointer,
|
||||
);
|
||||
let calldata_end_pointer = context.build_gep(
|
||||
input_pointer,
|
||||
&[calldata_size_casted],
|
||||
context
|
||||
.llvm()
|
||||
.ptr_type(AddressSpace::Generic.into())
|
||||
.as_basic_type_enum(),
|
||||
"return_data_abi_initializer",
|
||||
);
|
||||
context.write_abi_pointer(
|
||||
calldata_end_pointer,
|
||||
crate::polkavm::GLOBAL_RETURN_DATA_POINTER,
|
||||
);
|
||||
context.write_abi_pointer(calldata_end_pointer, crate::polkavm::GLOBAL_ACTIVE_POINTER);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
pub mod default_call;
|
||||
pub mod deploy_code;
|
||||
pub mod deployer_call;
|
||||
pub mod entry;
|
||||
pub mod runtime_code;
|
||||
|
||||
@@ -13,14 +12,13 @@ use crate::polkavm::Dependency;
|
||||
use crate::polkavm::WriteLLVM;
|
||||
|
||||
use self::default_call::DefaultCall;
|
||||
use self::deployer_call::DeployerCall;
|
||||
|
||||
/// The front-end runtime functions.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Runtime {
|
||||
/// The address space where the calldata is allocated.
|
||||
/// Solidity uses the ordinary heap. Vyper uses the auxiliary heap.
|
||||
address_space: AddressSpace,
|
||||
_address_space: AddressSpace,
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
@@ -34,8 +32,8 @@ impl Runtime {
|
||||
pub const FUNCTION_RUNTIME_CODE: &'static str = "__runtime";
|
||||
|
||||
/// A shortcut constructor.
|
||||
pub fn new(address_space: AddressSpace) -> Self {
|
||||
Self { address_space }
|
||||
pub fn new(_address_space: AddressSpace) -> Self {
|
||||
Self { _address_space }
|
||||
}
|
||||
|
||||
/// Returns the corresponding runtime function.
|
||||
@@ -52,18 +50,6 @@ impl Runtime {
|
||||
.borrow()
|
||||
.declaration()
|
||||
}
|
||||
|
||||
/// Returns the corresponding runtime function.
|
||||
pub fn deployer_call<'ctx, D>(context: &Context<'ctx, D>) -> FunctionDeclaration<'ctx>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
context
|
||||
.get_function(DeployerCall::FUNCTION_NAME)
|
||||
.expect("Always exists")
|
||||
.borrow()
|
||||
.declaration()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> WriteLLVM<D> for Runtime
|
||||
@@ -74,7 +60,6 @@ where
|
||||
//DefaultCall::new(context.llvm_runtime().far_call).declare(context)?;
|
||||
DefaultCall::new(context.llvm_runtime().static_call).declare(context)?;
|
||||
DefaultCall::new(context.llvm_runtime().delegate_call).declare(context)?;
|
||||
DeployerCall::new(self.address_space).declare(context)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -83,7 +68,6 @@ where
|
||||
//DefaultCall::new(context.llvm_runtime().far_call).into_llvm(context)?;
|
||||
DefaultCall::new(context.llvm_runtime().static_call).into_llvm(context)?;
|
||||
DefaultCall::new(context.llvm_runtime().delegate_call).into_llvm(context)?;
|
||||
DeployerCall::new(self.address_space).into_llvm(context)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -136,7 +136,9 @@ where
|
||||
module: &inkwell::module::Module<'ctx>,
|
||||
) {
|
||||
module
|
||||
.link_in_module(pallet_contracts_pvm_llapi::module(llvm, "polkavm_guest").unwrap())
|
||||
.link_in_module(
|
||||
pallet_contracts_pvm_llapi::polkavm_guest::module(llvm, "polkavm_guest").unwrap(),
|
||||
)
|
||||
.expect("the PolkaVM guest API module should be linkable");
|
||||
|
||||
for export in runtime_api::EXPORTS {
|
||||
@@ -164,7 +166,7 @@ where
|
||||
size: u32,
|
||||
) {
|
||||
module
|
||||
.link_in_module(pallet_contracts_pvm_llapi::min_stack_size(
|
||||
.link_in_module(pallet_contracts_pvm_llapi::polkavm_guest::min_stack_size(
|
||||
llvm,
|
||||
"polkavm_stack_size",
|
||||
size,
|
||||
@@ -1139,26 +1141,13 @@ where
|
||||
self.builder.build_unreachable().unwrap();
|
||||
}
|
||||
|
||||
/// Builds a long contract exit sequence.
|
||||
/// The deploy code does not return the runtime code like in EVM. Instead, it returns some
|
||||
/// additional contract metadata, e.g. the array of immutables.
|
||||
/// The deploy code uses the auxiliary heap for the return, because otherwise it is not possible
|
||||
/// to allocate memory together with the Yul allocator safely.
|
||||
/// Builds a contract exit sequence.
|
||||
pub fn build_exit(
|
||||
&self,
|
||||
flags: inkwell::values::IntValue<'ctx>,
|
||||
offset: inkwell::values::IntValue<'ctx>,
|
||||
length: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<()> {
|
||||
// TODO:
|
||||
//let return_forward_mode = if self.code_type() == Some(CodeType::Deploy)
|
||||
// && return_function == self.llvm_runtime().r#return
|
||||
//{
|
||||
// zkevm_opcode_defs::RetForwardPageType::UseAuxHeap
|
||||
//} else {
|
||||
// zkevm_opcode_defs::RetForwardPageType::UseHeap
|
||||
//};
|
||||
|
||||
let offset_truncated = self.safe_truncate_int_to_xlen(offset)?;
|
||||
let length_truncated = self.safe_truncate_int_to_xlen(length)?;
|
||||
let offset_into_heap = self.build_heap_gep(offset_truncated, length_truncated)?;
|
||||
|
||||
@@ -5,9 +5,9 @@ use num::Zero;
|
||||
|
||||
use crate::polkavm::context::argument::Argument;
|
||||
use crate::polkavm::context::code_type::CodeType;
|
||||
use crate::polkavm::context::function::runtime::Runtime;
|
||||
use crate::polkavm::context::Context;
|
||||
use crate::polkavm::Dependency;
|
||||
use crate::polkavm_const::runtime_api;
|
||||
|
||||
/// Translates the contract `create` instruction.
|
||||
/// The instruction is simulated by a call to a system contract.
|
||||
@@ -20,32 +20,10 @@ pub fn create<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let signature_hash_string =
|
||||
crate::polkavm::utils::keccak256(crate::polkavm::DEPLOYER_SIGNATURE_CREATE.as_bytes());
|
||||
let signature_hash = context.word_const_str_hex(signature_hash_string.as_str());
|
||||
|
||||
let salt = context.word_const(0);
|
||||
|
||||
let function = Runtime::deployer_call(context);
|
||||
let result = context
|
||||
.build_call(
|
||||
function,
|
||||
&[
|
||||
value.as_basic_value_enum(),
|
||||
input_offset.as_basic_value_enum(),
|
||||
input_length.as_basic_value_enum(),
|
||||
signature_hash.as_basic_value_enum(),
|
||||
salt.as_basic_value_enum(),
|
||||
],
|
||||
"create_deployer_call",
|
||||
)
|
||||
.expect("Always exists");
|
||||
|
||||
Ok(result)
|
||||
self::create2(context, value, input_offset, input_length, None)
|
||||
}
|
||||
|
||||
/// Translates the contract `create2` instruction.
|
||||
/// The instruction is simulated by a call to a system contract.
|
||||
pub fn create2<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
value: inkwell::values::IntValue<'ctx>,
|
||||
@@ -56,28 +34,72 @@ pub fn create2<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let signature_hash_string =
|
||||
crate::polkavm::utils::keccak256(crate::polkavm::DEPLOYER_SIGNATURE_CREATE2.as_bytes());
|
||||
let signature_hash = context.word_const_str_hex(signature_hash_string.as_str());
|
||||
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
|
||||
let input_length = context.safe_truncate_int_to_xlen(input_length)?;
|
||||
|
||||
let salt = salt.unwrap_or_else(|| context.word_const(0));
|
||||
let value_pointer = context.build_alloca(context.value_type(), "value");
|
||||
context.build_store(value_pointer, value)?;
|
||||
|
||||
let function = Runtime::deployer_call(context);
|
||||
let result = context
|
||||
.build_call(
|
||||
function,
|
||||
&[
|
||||
value.as_basic_value_enum(),
|
||||
input_offset.as_basic_value_enum(),
|
||||
input_length.as_basic_value_enum(),
|
||||
signature_hash.as_basic_value_enum(),
|
||||
salt.as_basic_value_enum(),
|
||||
],
|
||||
"create2_deployer_call",
|
||||
)
|
||||
.expect("Always exists");
|
||||
let code_hash_pointer = context.build_heap_gep(input_offset, input_length)?;
|
||||
|
||||
Ok(result)
|
||||
let input_data_pointer = context.build_gep(
|
||||
code_hash_pointer,
|
||||
&[context
|
||||
.xlen_type()
|
||||
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false)],
|
||||
context.byte_type(),
|
||||
"value_ptr_parameter_offset",
|
||||
);
|
||||
|
||||
let salt_pointer = context.build_alloca(context.word_type(), "salt");
|
||||
context.build_store(salt_pointer, salt.unwrap_or_else(|| context.word_const(0)))?;
|
||||
|
||||
let (address_pointer, address_length_pointer) =
|
||||
context.build_stack_parameter(revive_common::BIT_LENGTH_ETH_ADDRESS, "address_pointer");
|
||||
|
||||
let sentinel = context
|
||||
.xlen_type()
|
||||
.const_all_ones()
|
||||
.const_to_pointer(context.llvm().ptr_type(Default::default()));
|
||||
|
||||
let argument_pointer = pallet_contracts_pvm_llapi::calling_convention::Spill::new(
|
||||
context.builder(),
|
||||
pallet_contracts_pvm_llapi::calling_convention::instantiate(context.llvm()),
|
||||
"create2_arguments",
|
||||
)?
|
||||
.next(code_hash_pointer.value)?
|
||||
.skip()
|
||||
.skip()
|
||||
.next(sentinel)?
|
||||
.next(value_pointer.value)?
|
||||
.next(input_data_pointer.value)?
|
||||
.next(input_length)?
|
||||
.next(address_pointer.value)?
|
||||
.next(address_length_pointer.value)?
|
||||
.next(sentinel)?
|
||||
.next(sentinel)?
|
||||
.next(salt_pointer.value)?
|
||||
.next(
|
||||
context
|
||||
.xlen_type()
|
||||
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false),
|
||||
)?
|
||||
.done();
|
||||
|
||||
context.builder().build_direct_call(
|
||||
context.runtime_api_method(runtime_api::INSTANTIATE),
|
||||
&[context
|
||||
.builder()
|
||||
.build_ptr_to_int(argument_pointer, context.xlen_type(), "argument_pointer")?
|
||||
.into()],
|
||||
"create2",
|
||||
)?;
|
||||
|
||||
context.build_load_word(
|
||||
address_pointer,
|
||||
revive_common::BIT_LENGTH_ETH_ADDRESS,
|
||||
"address",
|
||||
)
|
||||
}
|
||||
|
||||
/// Translates the contract hash instruction, which is actually used to set the hash of the contract
|
||||
@@ -121,15 +143,8 @@ where
|
||||
Ok(Argument::new_with_original(hash_value, hash_string))
|
||||
}
|
||||
|
||||
/// Translates the deployer call header size instruction, Usually, the header consists of:
|
||||
/// - the deployer contract method signature
|
||||
/// - the salt if the call is `create2`, or zero if the call is `create1`
|
||||
/// - the hash of the bytecode of the contract whose instance is being created
|
||||
/// - the offset of the constructor arguments
|
||||
/// - the length of the constructor arguments
|
||||
/// If the call is `create1`, the space for the salt is still allocated, because the memory for the
|
||||
/// header is allocated by the Yul or EVM legacy assembly before it is known which version of
|
||||
/// `create` is going to be used.
|
||||
/// Translates the deploy call header size instruction. the header consists of
|
||||
/// the hash of the bytecode of the contract whose instance is being created.
|
||||
/// Represents `datasize` in Yul and `PUSH #[$]` in the EVM legacy assembly.
|
||||
pub fn header_size<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
//! Translates the transaction return operations.
|
||||
|
||||
use crate::polkavm::context::address_space::AddressSpace;
|
||||
use crate::polkavm::context::code_type::CodeType;
|
||||
use crate::polkavm::context::pointer::Pointer;
|
||||
use crate::polkavm::context::Context;
|
||||
use crate::polkavm::Dependency;
|
||||
|
||||
/// Translates the `return` instruction.
|
||||
/// Unlike in EVM, zkSync constructors return the array of contract immutables.
|
||||
pub fn r#return<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
offset: inkwell::values::IntValue<'ctx>,
|
||||
@@ -16,66 +12,15 @@ pub fn r#return<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
match context.code_type() {
|
||||
None => {
|
||||
anyhow::bail!("Return is not available if the contract part is undefined");
|
||||
}
|
||||
Some(CodeType::Deploy) => {
|
||||
let immutables_offset_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::HeapAuxiliary,
|
||||
context.word_type(),
|
||||
context.word_const(crate::polkavm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA),
|
||||
"immutables_offset_pointer",
|
||||
);
|
||||
context.build_store(
|
||||
immutables_offset_pointer,
|
||||
context.word_const(revive_common::BYTE_LENGTH_WORD as u64),
|
||||
)?;
|
||||
|
||||
let immutables_number_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::HeapAuxiliary,
|
||||
context.word_type(),
|
||||
context.word_const(
|
||||
crate::polkavm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA
|
||||
+ (revive_common::BYTE_LENGTH_WORD as u64),
|
||||
),
|
||||
"immutables_number_pointer",
|
||||
);
|
||||
let immutable_values_size = context.immutables_size()?;
|
||||
context.build_store(
|
||||
immutables_number_pointer,
|
||||
context
|
||||
.word_const((immutable_values_size / revive_common::BYTE_LENGTH_WORD) as u64),
|
||||
)?;
|
||||
let immutables_size = context.builder().build_int_mul(
|
||||
context.word_const(immutable_values_size as u64),
|
||||
context.word_const(2),
|
||||
"immutables_size",
|
||||
)?;
|
||||
let return_data_length = context.builder().build_int_add(
|
||||
immutables_size,
|
||||
context.word_const((revive_common::BYTE_LENGTH_WORD * 2) as u64),
|
||||
"return_data_length",
|
||||
)?;
|
||||
|
||||
context.build_exit(
|
||||
context.integer_const(crate::polkavm::XLEN, 0),
|
||||
context.word_const(crate::polkavm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA),
|
||||
return_data_length,
|
||||
)?;
|
||||
}
|
||||
Some(CodeType::Runtime) => {
|
||||
context.build_exit(
|
||||
context.integer_const(crate::polkavm::XLEN, 0),
|
||||
offset,
|
||||
length,
|
||||
)?;
|
||||
}
|
||||
if context.code_type().is_none() {
|
||||
anyhow::bail!("Return is not available if the contract part is undefined");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
context.build_exit(
|
||||
context.integer_const(crate::polkavm::XLEN, 0),
|
||||
offset,
|
||||
length,
|
||||
)
|
||||
}
|
||||
|
||||
/// Translates the `revert` instruction.
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
//! Translates the return data instructions.
|
||||
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::polkavm::context::address_space::AddressSpace;
|
||||
use crate::polkavm::context::pointer::Pointer;
|
||||
use crate::polkavm::context::Context;
|
||||
use crate::polkavm::Dependency;
|
||||
|
||||
@@ -15,13 +12,19 @@ pub fn size<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
match context.get_global_value(crate::polkavm::GLOBAL_RETURN_DATA_SIZE) {
|
||||
Ok(global) => Ok(global),
|
||||
Err(_error) => Ok(context.word_const(0).as_basic_value_enum()),
|
||||
}
|
||||
let value = context
|
||||
.get_global_value(crate::polkavm::GLOBAL_RETURN_DATA_SIZE)?
|
||||
.into_int_value();
|
||||
Ok(context
|
||||
.builder()
|
||||
.build_int_z_extend(value, context.word_type(), "calldatasize_extended")?
|
||||
.as_basic_value_enum())
|
||||
}
|
||||
|
||||
/// Translates the return data copy.
|
||||
/// Translates the return data copy, trapping if
|
||||
/// - Destination, offset or size exceed the VM register size (XLEN)
|
||||
/// - `source_offset + size` overflows (in XLEN)
|
||||
/// - `source_offset + size` is beyond `RETURNDATASIZE`
|
||||
pub fn copy<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
destination_offset: inkwell::values::IntValue<'ctx>,
|
||||
@@ -31,57 +34,59 @@ pub fn copy<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let error_block = context.append_basic_block("return_data_copy_error_block");
|
||||
let join_block = context.append_basic_block("return_data_copy_join_block");
|
||||
let source_offset = context.safe_truncate_int_to_xlen(source_offset)?;
|
||||
let destination_offset = context.safe_truncate_int_to_xlen(destination_offset)?;
|
||||
let size = context.safe_truncate_int_to_xlen(size)?;
|
||||
|
||||
let return_data_size = self::size(context)?.into_int_value();
|
||||
let copy_slice_end =
|
||||
context
|
||||
.builder()
|
||||
.build_int_add(source_offset, size, "return_data_copy_slice_end")?;
|
||||
let is_copy_out_of_bounds = context.builder().build_int_compare(
|
||||
let block_copy = context.append_basic_block("copy_block");
|
||||
let block_trap = context.append_basic_block("trap_block");
|
||||
let block_check_out_of_bounds = context.append_basic_block("check_out_of_bounds_block");
|
||||
let is_overflow = context.builder().build_int_compare(
|
||||
inkwell::IntPredicate::UGT,
|
||||
copy_slice_end,
|
||||
return_data_size,
|
||||
"return_data_copy_is_out_of_bounds",
|
||||
source_offset,
|
||||
context.builder().build_int_sub(
|
||||
context.xlen_type().const_all_ones(),
|
||||
size,
|
||||
"offset_plus_size_max_value",
|
||||
)?,
|
||||
"is_returndata_size_out_of_bounds",
|
||||
)?;
|
||||
context.build_conditional_branch(is_copy_out_of_bounds, error_block, join_block)?;
|
||||
context.build_conditional_branch(is_overflow, block_trap, block_check_out_of_bounds)?;
|
||||
|
||||
context.set_basic_block(error_block);
|
||||
crate::polkavm::evm::r#return::revert(context, context.word_const(0), context.word_const(0))?;
|
||||
context.set_basic_block(block_check_out_of_bounds);
|
||||
let is_out_of_bounds = context.builder().build_int_compare(
|
||||
inkwell::IntPredicate::UGT,
|
||||
context.builder().build_int_add(
|
||||
source_offset,
|
||||
context
|
||||
.get_global_value(crate::polkavm::GLOBAL_RETURN_DATA_SIZE)?
|
||||
.into_int_value(),
|
||||
"returndata_end_pointer",
|
||||
)?,
|
||||
context
|
||||
.xlen_type()
|
||||
.const_int(crate::PolkaVMEntryFunction::MAX_CALLDATA_SIZE as u64, false),
|
||||
"is_return_data_copy_overflow",
|
||||
)?;
|
||||
context.build_conditional_branch(is_out_of_bounds, block_trap, block_copy)?;
|
||||
|
||||
context.set_basic_block(join_block);
|
||||
let destination = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::Heap,
|
||||
context.byte_type(),
|
||||
destination_offset,
|
||||
"return_data_copy_destination_pointer",
|
||||
);
|
||||
|
||||
let return_data_pointer_global =
|
||||
context.get_global(crate::polkavm::GLOBAL_RETURN_DATA_POINTER)?;
|
||||
let return_data_pointer_pointer = return_data_pointer_global.into();
|
||||
let return_data_pointer =
|
||||
context.build_load(return_data_pointer_pointer, "return_data_pointer")?;
|
||||
let source = context.build_gep(
|
||||
Pointer::new(
|
||||
context.byte_type(),
|
||||
return_data_pointer_pointer.address_space,
|
||||
return_data_pointer.into_pointer_value(),
|
||||
),
|
||||
&[source_offset],
|
||||
context.byte_type().as_basic_type_enum(),
|
||||
"return_data_source_pointer",
|
||||
);
|
||||
context.set_basic_block(block_trap);
|
||||
context.build_call(context.intrinsics().trap, &[], "invalid_returndata_copy");
|
||||
context.build_unreachable();
|
||||
|
||||
context.set_basic_block(block_copy);
|
||||
context.build_memcpy(
|
||||
context.intrinsics().memory_copy_from_generic,
|
||||
destination,
|
||||
source,
|
||||
context.build_heap_gep(destination_offset, size)?,
|
||||
context.build_gep(
|
||||
context
|
||||
.get_global(crate::polkavm::GLOBAL_RETURN_DATA_POINTER)?
|
||||
.into(),
|
||||
&[context.xlen_type().const_zero(), source_offset],
|
||||
context.byte_type(),
|
||||
"source_offset_gep",
|
||||
),
|
||||
size,
|
||||
"return_data_copy_memcpy_from_return_data",
|
||||
)?;
|
||||
|
||||
todo!("Build heap GEP to allocate if necessary")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ pub mod metadata_hash;
|
||||
pub mod utils;
|
||||
|
||||
pub use self::r#const::*;
|
||||
use self::utils::keccak256;
|
||||
|
||||
use crate::debug_config::DebugConfig;
|
||||
use crate::optimizer::settings::Settings as OptimizerSettings;
|
||||
@@ -56,7 +57,7 @@ pub fn build_assembly_text(
|
||||
assembly_text.to_owned(),
|
||||
metadata_hash,
|
||||
bytecode.to_owned(),
|
||||
Default::default(),
|
||||
keccak256(bytecode),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -4,4 +4,5 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
inkwell = { workspace = true, features = ["target-riscv", "no-libffi-linking", "llvm18-0"] }
|
||||
@@ -0,0 +1,90 @@
|
||||
use inkwell::{
|
||||
builder::Builder,
|
||||
context::Context,
|
||||
types::{BasicType, StructType},
|
||||
values::{BasicValue, PointerValue},
|
||||
};
|
||||
|
||||
pub struct Spill<'ctx> {
|
||||
pointer: PointerValue<'ctx>,
|
||||
builder: &'ctx Builder<'ctx>,
|
||||
r#type: StructType<'ctx>,
|
||||
current_field: u32,
|
||||
}
|
||||
|
||||
impl<'ctx> Spill<'ctx> {
|
||||
pub fn new(
|
||||
builder: &'ctx Builder<'ctx>,
|
||||
r#type: StructType<'ctx>,
|
||||
name: &str,
|
||||
) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
pointer: builder.build_alloca(r#type, name)?,
|
||||
builder,
|
||||
r#type,
|
||||
current_field: 0,
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instantiate(context: &Context) -> StructType {
|
||||
context.struct_type(
|
||||
&[
|
||||
// code_hash_ptr: u32,
|
||||
context.ptr_type(Default::default()).as_basic_type_enum(),
|
||||
// ref_time_limit: u64,
|
||||
context.i64_type().as_basic_type_enum(),
|
||||
// proof_size_limit: u64,
|
||||
context.i64_type().as_basic_type_enum(),
|
||||
// deposit_ptr: u32,
|
||||
context.ptr_type(Default::default()).as_basic_type_enum(),
|
||||
// value_ptr: u32,
|
||||
context.ptr_type(Default::default()).as_basic_type_enum(),
|
||||
// input_data_ptr: u32,
|
||||
context.ptr_type(Default::default()).as_basic_type_enum(),
|
||||
// input_data_len: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// address_ptr: u32,
|
||||
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,
|
||||
context.ptr_type(Default::default()).as_basic_type_enum(),
|
||||
// output_len_ptr: u32,
|
||||
context.ptr_type(Default::default()).as_basic_type_enum(),
|
||||
// salt_ptr: u32,
|
||||
context.ptr_type(Default::default()).as_basic_type_enum(),
|
||||
// salt_len: u32
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
],
|
||||
true,
|
||||
)
|
||||
}
|
||||
@@ -1,52 +1,2 @@
|
||||
//! This crate vendors the [PolkaVM][0] C API and provides a LLVM module for interacting
|
||||
//! with the `pallet-contracts` runtime API.
|
||||
//! At present, the contracts pallet requires blobs to export `call` and `deploy`,
|
||||
//! and offers a bunch of [runtime API methods][1]. The provided [module] implements
|
||||
//! those exports and imports.
|
||||
//! [0]: [https://crates.io/crates/polkavm]
|
||||
//! [1]: [https://docs.rs/pallet-contracts/26.0.0/pallet_contracts/api_doc/index.html]
|
||||
|
||||
use inkwell::{context::Context, memory_buffer::MemoryBuffer, module::Module, support::LLVMString};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/polkavm_guest.rs"));
|
||||
|
||||
/// Creates a LLVM module from the [BITCODE].
|
||||
/// The module does:
|
||||
/// - Export the `call` and `deploy` functions (which are named thereafter).
|
||||
/// - Import (most) `pallet-contracts` runtime API functions.
|
||||
/// Returns `Error` if the bitcode fails to parse, which should never happen.
|
||||
pub fn module<'context>(
|
||||
context: &'context Context,
|
||||
module_name: &str,
|
||||
) -> Result<Module<'context>, LLVMString> {
|
||||
let buf = MemoryBuffer::create_from_memory_range(BITCODE, module_name);
|
||||
Module::parse_bitcode_from_buffer(&buf, context)
|
||||
}
|
||||
|
||||
/// Creates a module that sets the PolkaVM minimum stack size to [`size`] if linked in.
|
||||
pub fn min_stack_size<'context>(
|
||||
context: &'context Context,
|
||||
module_name: &str,
|
||||
size: u32,
|
||||
) -> Module<'context> {
|
||||
let module = context.create_module(module_name);
|
||||
module.set_inline_assembly(&format!(
|
||||
".pushsection .polkavm_min_stack_size,\"\",@progbits
|
||||
.word {size}
|
||||
.popsection"
|
||||
));
|
||||
module
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
inkwell::targets::Target::initialize_riscv(&Default::default());
|
||||
let context = inkwell::context::Context::create();
|
||||
let module = crate::module(&context, "polkavm_guest").unwrap();
|
||||
|
||||
assert!(module.get_function("call").is_some());
|
||||
assert!(module.get_function("deploy").is_some());
|
||||
}
|
||||
}
|
||||
pub mod calling_convention;
|
||||
pub mod polkavm_guest;
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
//! This crate vendors the [PolkaVM][0] C API and provides a LLVM module for interacting
|
||||
//! with the `pallet-contracts` runtime API.
|
||||
//! At present, the contracts pallet requires blobs to export `call` and `deploy`,
|
||||
//! and offers a bunch of [runtime API methods][1]. The provided [module] implements
|
||||
//! those exports and imports.
|
||||
//! [0]: [https://crates.io/crates/polkavm]
|
||||
//! [1]: [https://docs.rs/pallet-contracts/26.0.0/pallet_contracts/api_doc/index.html]
|
||||
|
||||
use inkwell::{context::Context, memory_buffer::MemoryBuffer, module::Module, support::LLVMString};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/polkavm_guest.rs"));
|
||||
|
||||
/// Creates a LLVM module from the [BITCODE].
|
||||
/// The module does:
|
||||
/// - Export the `call` and `deploy` functions (which are named thereafter).
|
||||
/// - Import (most) `pallet-contracts` runtime API functions.
|
||||
/// Returns `Error` if the bitcode fails to parse, which should never happen.
|
||||
pub fn module<'context>(
|
||||
context: &'context Context,
|
||||
module_name: &str,
|
||||
) -> Result<Module<'context>, LLVMString> {
|
||||
let buf = MemoryBuffer::create_from_memory_range(BITCODE, module_name);
|
||||
Module::parse_bitcode_from_buffer(&buf, context)
|
||||
}
|
||||
|
||||
/// Creates a module that sets the PolkaVM minimum stack size to [`size`] if linked in.
|
||||
pub fn min_stack_size<'context>(
|
||||
context: &'context Context,
|
||||
module_name: &str,
|
||||
size: u32,
|
||||
) -> Module<'context> {
|
||||
let module = context.create_module(module_name);
|
||||
module.set_inline_assembly(&format!(
|
||||
".pushsection .polkavm_min_stack_size,\"\",@progbits
|
||||
.word {size}
|
||||
.popsection"
|
||||
));
|
||||
module
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::polkavm_guest;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
inkwell::targets::Target::initialize_riscv(&Default::default());
|
||||
let context = inkwell::context::Context::create();
|
||||
let module = polkavm_guest::module(&context, "polkavm_guest").unwrap();
|
||||
|
||||
assert!(module.get_function("call").is_some());
|
||||
assert!(module.get_function("deploy").is_some());
|
||||
}
|
||||
}
|
||||
@@ -875,16 +875,9 @@ impl FunctionCall {
|
||||
}
|
||||
Name::DataCopy => {
|
||||
let arguments = self.pop_arguments_llvm::<D, 3>(context)?;
|
||||
let offset = context.builder().build_int_add(
|
||||
arguments[0].into_int_value(),
|
||||
context.word_const(
|
||||
(revive_common::BYTE_LENGTH_X32 + revive_common::BYTE_LENGTH_WORD) as u64,
|
||||
),
|
||||
"datacopy_contract_hash_offset",
|
||||
)?;
|
||||
revive_llvm_context::polkavm_evm_memory::store(
|
||||
context,
|
||||
offset,
|
||||
arguments[0].into_int_value(),
|
||||
arguments[1].into_int_value(),
|
||||
)
|
||||
.map(|_| None)
|
||||
|
||||
Reference in New Issue
Block a user