implement calldatacopy

Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
xermicus
2024-03-19 10:49:47 +01:00
parent 6d058a42ed
commit 2b9e40225d
12 changed files with 210 additions and 43 deletions
+2
View File
@@ -4,3 +4,5 @@
.DS_Store
/*.sol
/*.yul
/*.ll
/*.s
Generated
+56 -1
View File
@@ -55,6 +55,36 @@ dependencies = [
"bytes",
]
[[package]]
name = "alloy-sol-macro"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86ec0a47740b20bc5613b8712d0d321d031c4efc58e9645af96085d5cccfc27"
dependencies = [
"const-hex",
"dunce",
"heck 0.4.1",
"indexmap",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.52",
"syn-solidity",
"tiny-keccak",
]
[[package]]
name = "alloy-sol-types"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad09ec5853fa700d12d778ad224dcdec636af424d29fad84fb9a2f16a5b0ef09"
dependencies = [
"alloy-primitives",
"alloy-sol-macro",
"const-hex",
"serde",
]
[[package]]
name = "anyhow"
version = "1.0.81"
@@ -470,6 +500,12 @@ dependencies = [
"subtle",
]
[[package]]
name = "dunce"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
[[package]]
name = "ecdsa"
version = "0.16.9"
@@ -711,6 +747,12 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hex"
version = "0.4.3"
@@ -1378,6 +1420,7 @@ name = "revive-integration"
version = "0.1.0"
dependencies = [
"alloy-primitives",
"alloy-sol-types",
"era-compiler-llvm-context",
"hex",
"parity-scale-codec",
@@ -1737,7 +1780,7 @@ version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
dependencies = [
"heck",
"heck 0.3.3",
"proc-macro-error",
"proc-macro2",
"quote",
@@ -1772,6 +1815,18 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn-solidity"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb3d0961cd53c23ea94eeec56ba940f636f6394788976e9f16ca5ee0aca7464a"
dependencies = [
"paste",
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]]
name = "tap"
version = "1.0.1"
+1
View File
@@ -31,6 +31,7 @@ polkavm-linker = { git = "https://github.com/koute/polkavm.git" }
polkavm = { git = "https://github.com/koute/polkavm.git" }
parity-scale-codec = "3.6"
alloy-primitives = "0.6"
alloy-sol-types = "0.6"
[workspace.dependencies.inkwell]
git = "https://github.com/TheDan64/inkwell.git"
+3 -2
View File
@@ -2,7 +2,7 @@
YUL and EVM bytecode recompiler to LLVM, targetting RISC-V on PolkaVM.
Code bases of [frontend](https://github.com/matter-labs/era-compiler-solidity) and [code generator](https://github.com/matter-labs/era-compiler-llvm-context) are forked adapted from ZKSync `zksolc`.
Code bases of [frontend](https://github.com/matter-labs/era-compiler-solidity) and [code generator](https://github.com/matter-labs/era-compiler-llvm-context) are forked and adapted from ZKSync `zksolc`.
# Status
@@ -12,8 +12,9 @@ Currently, primary goal of this codebase is to allow for benchmarks comparing pe
The project is in a very early PoC phase. Don't yet expect the produced code to be working nor to be correct for anything more than a basic flipper contract at the current stage.
- [ ] Efficient implementations of byte swaps, memset, memmove and the like
- [ ] Efficient implementations of byte swaps, memset, memmove, mulmod and the like
- [ ] Use `drink` for integration tests once we have 64bit support in PolkaVM
- [ ] Use PolkaVM allocator for heap space
- [ ] Exercice `schlau` and possibly `smart-bench` benchmark cases
- [ ] 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
+2 -1
View File
@@ -11,4 +11,5 @@ polkavm = { workspace = true }
parity-scale-codec = { workspace = true }
revive-solidity = { path = "../solidity" }
era-compiler-llvm-context = { path = "../llvm-context" }
alloy-primitives = { workspace = true }
alloy-primitives = { workspace = true }
alloy-sol-types = { workspace = true }
+39 -9
View File
@@ -27,7 +27,8 @@ pub fn compile_blob(contract_name: &str, source_code: &str) -> Vec<u8> {
#[cfg(test)]
mod tests {
use alloy_primitives::U256;
use alloy_primitives::{FixedBytes, Keccak256, I256, U256};
use alloy_sol_types::{sol, SolCall};
use crate::mock_runtime::{self, State};
@@ -46,6 +47,37 @@ mod tests {
assert_eq!(state.storage[&U256::ZERO], U256::ZERO);
}
#[test]
fn hash_keccak_256() {
sol!(
#[derive(Debug, PartialEq, Eq)]
contract TestSha3 {
function test(string memory _pre) external payable returns (bytes32);
}
);
let source = r#"contract TestSha3 {
function test(string memory _pre) external payable returns (bytes32 hash) {
hash = keccak256(bytes(_pre));
}
}"#;
let code = crate::compile_blob("TestSha3", source);
let param = "hello";
let input = TestSha3::testCall::new((param.to_string(),)).abi_encode();
let state = State::new(input);
let (instance, export) = mock_runtime::prepare(&code);
let state = crate::mock_runtime::call(state, &instance, export);
assert_eq!(state.output.flags, 0);
let mut hasher = Keccak256::new();
hasher.update(param);
let expected = hasher.finalize();
let received = FixedBytes::<32>::from_slice(&state.output.data);
assert_eq!(expected, received);
}
#[test]
fn erc20() {
let _ = crate::compile_blob("ERC20", include_str!("../contracts/ERC20.sol"));
@@ -54,8 +86,8 @@ mod tests {
#[test]
fn triangle_number() {
let code = crate::compile_blob("Computation", include_str!("../contracts/Computation.sol"));
let param = alloy_primitives::U256::try_from(13).unwrap();
let expected = alloy_primitives::U256::try_from(91).unwrap();
let param = U256::try_from(13).unwrap();
let expected = U256::try_from(91).unwrap();
// function triangle_number(int64)
let mut input = 0x0f760610u32.to_be_bytes().to_vec();
@@ -67,16 +99,15 @@ mod tests {
assert_eq!(state.output.flags, 0);
let received =
alloy_primitives::U256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
let received = U256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
assert_eq!(received, expected);
}
#[test]
fn odd_product() {
let code = crate::compile_blob("Computation", include_str!("../contracts/Computation.sol"));
let param = alloy_primitives::I256::try_from(5i32).unwrap();
let expected = alloy_primitives::I256::try_from(945i64).unwrap();
let param = I256::try_from(5i32).unwrap();
let expected = I256::try_from(945i64).unwrap();
// function odd_product(int32)
let mut input = 0x00261b66u32.to_be_bytes().to_vec();
@@ -88,8 +119,7 @@ mod tests {
assert_eq!(state.output.flags, 0);
let received =
alloy_primitives::I256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
let received = I256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
assert_eq!(received, expected);
}
}
+22 -1
View File
@@ -2,7 +2,7 @@
//! TODO: Switch to drink! once RISCV is ready in polkadot-sdk
use std::collections::HashMap;
use alloy_primitives::U256;
use alloy_primitives::{Keccak256, U256};
use parity_scale_codec::Encode;
use polkavm::{
Caller, Config, Engine, ExportIndex, GasMeteringKind, InstancePre, Linker, Module,
@@ -168,6 +168,27 @@ fn link_host_functions(engine: &Engine) -> Linker<State> {
)
.unwrap();
linker
.func_wrap(
"hash_keccak_256",
|caller: Caller<State>,
input_ptr: u32,
input_len: u32,
out_ptr: u32|
-> Result<(), Trap> {
let (mut caller, _) = caller.split();
let pre = caller.read_memory_into_vec(input_ptr, input_len)?;
let mut hasher = Keccak256::new();
hasher.update(&pre);
caller.write_memory(out_ptr, &hasher.finalize()[..])?;
Ok(())
},
)
.unwrap();
linker
}
+8 -9
View File
@@ -671,15 +671,13 @@ where
"offset_ptrtoint",
)?;
let pointer_value = unsafe {
self.builder
.build_gep(
self.byte_type(),
heap_pointer.value.as_pointer_value(),
&[offset],
"heap_offset_via_gep",
)
.unwrap()
};
self.builder.build_gep(
self.byte_type(),
heap_pointer.value.as_pointer_value(),
&[offset],
"heap_offset_via_gep",
)
}?;
let value = self
.builder()
.build_load(pointer.r#type, pointer_value, name)?;
@@ -806,6 +804,7 @@ where
)
.unwrap()
};
let value = self.build_byte_swap(value.as_basic_value_enum());
let instruction = self.builder.build_store(pointer_value, value).unwrap();
@@ -78,6 +78,7 @@ impl<'ctx> Pointer<'ctx> {
"Stack pointers cannot be addressed"
);
let offset = context.safe_truncate_int_to_i32(offset).unwrap();
let value = context
.builder
.build_int_to_ptr(
+23 -16
View File
@@ -59,29 +59,36 @@ pub fn copy<'ctx, D>(
where
D: Dependency + Clone,
{
// TODO: Untested
let destination = Pointer::new_with_offset(
context,
AddressSpace::Heap,
context.byte_type(),
destination_offset,
"calldata_copy_destination_pointer",
);
let heap_pointer = context
.get_global(crate::eravm::GLOBAL_HEAP_MEMORY_POINTER)?
.value
.as_pointer_value();
let destination = unsafe {
context.builder().build_gep(
context.byte_type(),
heap_pointer,
&[destination_offset],
"calldata_pointer_with_offset",
)
}?;
let calldata_pointer = context
.get_global(crate::eravm::GLOBAL_CALLDATA_POINTER)?
.value
.as_pointer_value();
let source = context.build_gep(
Pointer::new(context.byte_type(), AddressSpace::Stack, calldata_pointer),
&[source_offset],
context.field_type().as_basic_type_enum(),
"calldata_pointer_with_offset",
);
let source = unsafe {
context.builder().build_gep(
context.byte_type(),
calldata_pointer,
&[source_offset],
"calldata_pointer_with_offset",
)
}?;
context.build_memcpy(
context.intrinsics().memory_copy_from_generic,
destination,
source,
Pointer::new(context.byte_type(), AddressSpace::Stack, destination),
Pointer::new(context.byte_type(), AddressSpace::Stack, source),
size,
"calldata_copy_memcpy_from_child",
)
+44 -4
View File
@@ -9,12 +9,52 @@ use crate::eravm::Dependency;
/// Translates the `sha3` instruction.
///
pub fn sha3<'ctx, D>(
_context: &mut Context<'ctx, D>,
_offset: inkwell::values::IntValue<'ctx>,
_length: inkwell::values::IntValue<'ctx>,
context: &mut Context<'ctx, D>,
offset: inkwell::values::IntValue<'ctx>,
length: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
let offset_casted = context.safe_truncate_int_to_i32(offset)?;
let heap_pointer = context.get_global(crate::eravm::GLOBAL_HEAP_MEMORY_POINTER)?;
let input_pointer = unsafe {
context.builder().build_gep(
context.byte_type(),
heap_pointer.value.as_pointer_value(),
&[offset_casted],
"heap_offset_via_gep",
)
}?;
let input_pointer_casted = context.builder().build_ptr_to_int(
input_pointer,
context.integer_type(32),
"input_pointer_casted",
)?;
let length_casted = context.safe_truncate_int_to_i32(length)?;
let output_pointer = context.build_alloca(context.field_type(), "output_pointer");
let output_pointer_casted = context.builder().build_ptr_to_int(
output_pointer.value,
context.integer_type(32),
"output_pointer_casted",
)?;
let function = context
.module()
.get_function("hash_keccak_256")
.expect("is declared");
context.builder().build_call(
function,
&[
input_pointer_casted.into(),
length_casted.into(),
output_pointer_casted.into(),
],
"call_seal_hash_keccak_256",
)?;
Ok(context.build_byte_swap(context.build_load(output_pointer, "sha3_output")?))
}
@@ -12,6 +12,15 @@ void * memset(void *b, int c, size_t len) {
return b;
}
void * memcpy(void *dst, const void *_src, size_t len) {
uint8_t *dest = dst;
const uint8_t *src = _src;
while (len--) *dest++ = *src++;
return dst;
}
// Exports