mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-14 08:51:02 +00:00
implement block.number and block.timestamp
Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
Generated
+1
@@ -1745,6 +1745,7 @@ dependencies = [
|
|||||||
"env_logger",
|
"env_logger",
|
||||||
"hex",
|
"hex",
|
||||||
"polkavm",
|
"polkavm",
|
||||||
|
"revive-common",
|
||||||
"revive-differential",
|
"revive-differential",
|
||||||
"revive-llvm-context",
|
"revive-llvm-context",
|
||||||
"revive-solidity",
|
"revive-solidity",
|
||||||
|
|||||||
@@ -21,3 +21,11 @@ pub const BIT_LENGTH_WORD: usize = crate::byte_length::BYTE_LENGTH_WORD * BIT_LE
|
|||||||
|
|
||||||
/// Bit length of the runtime value type.
|
/// Bit length of the runtime value type.
|
||||||
pub const BIT_LENGTH_VALUE: usize = crate::byte_length::BYTE_LENGTH_VALUE * BIT_LENGTH_BYTE;
|
pub const BIT_LENGTH_VALUE: usize = crate::byte_length::BYTE_LENGTH_VALUE * BIT_LENGTH_BYTE;
|
||||||
|
|
||||||
|
/// Bit length of thre runimte block number type.
|
||||||
|
pub const BIT_LENGTH_BLOCK_NUMBER: usize =
|
||||||
|
crate::byte_length::BYTE_LENGTH_BLOCK_NUMBER * BIT_LENGTH_BYTE;
|
||||||
|
|
||||||
|
/// Bit length of thre runimte block timestamp type.
|
||||||
|
pub const BIT_LENGTH_BLOCK_TIMESTAMP: usize =
|
||||||
|
crate::byte_length::BYTE_LENGTH_BLOCK_TIMESTAMP * BIT_LENGTH_BYTE;
|
||||||
|
|||||||
@@ -20,3 +20,9 @@ pub const BYTE_LENGTH_WORD: usize = 32;
|
|||||||
|
|
||||||
/// Byte length of the runtime value type.
|
/// Byte length of the runtime value type.
|
||||||
pub const BYTE_LENGTH_VALUE: usize = 16;
|
pub const BYTE_LENGTH_VALUE: usize = 16;
|
||||||
|
|
||||||
|
/// Byte length of the runtime block number type.
|
||||||
|
pub const BYTE_LENGTH_BLOCK_NUMBER: usize = 8;
|
||||||
|
|
||||||
|
/// Byte length of the runtime block timestamp type.
|
||||||
|
pub const BYTE_LENGTH_BLOCK_TIMESTAMP: usize = 8;
|
||||||
|
|||||||
@@ -7,4 +7,4 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
evm-interpreter = { workspace = true }
|
evm-interpreter = { workspace = true }
|
||||||
primitive-types = { workspace = true }
|
primitive-types = { workspace = true }
|
||||||
@@ -16,13 +16,13 @@ impl RuntimeEnvironment for UnimplementedHandler {
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn block_number(&self) -> U256 {
|
fn block_number(&self) -> U256 {
|
||||||
unimplemented!()
|
U256::from(123)
|
||||||
}
|
}
|
||||||
fn block_coinbase(&self) -> H160 {
|
fn block_coinbase(&self) -> H160 {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn block_timestamp(&self) -> U256 {
|
fn block_timestamp(&self) -> U256 {
|
||||||
unimplemented!()
|
U256::from(456)
|
||||||
}
|
}
|
||||||
fn block_difficulty(&self) -> U256 {
|
fn block_difficulty(&self) -> U256 {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ env_logger = { workspace = true }
|
|||||||
revive-solidity = { path = "../solidity" }
|
revive-solidity = { path = "../solidity" }
|
||||||
revive-differential = { path = "../differential" }
|
revive-differential = { path = "../differential" }
|
||||||
revive-llvm-context = { path = "../llvm-context" }
|
revive-llvm-context = { path = "../llvm-context" }
|
||||||
|
revive-common = { path = "../common" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
sha1 = { workspace = true }
|
sha1 = { workspace = true }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"ERC20": 53171,
|
"Baseline": 3917,
|
||||||
"Baseline": 3912,
|
"Computation": 7363,
|
||||||
"Flipper": 4353,
|
"ERC20": 52714,
|
||||||
"Fibonacci": 5971,
|
"Fibonacci": 5965,
|
||||||
"Computation": 7380
|
"Flipper": 4336
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.8.24;
|
||||||
|
|
||||||
|
contract Block {
|
||||||
|
function timestamp() public view returns (uint ret) {
|
||||||
|
ret = block.timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
function number() public view returns (uint ret) {
|
||||||
|
ret = block.number;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,6 +67,14 @@ sol!(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
sol!(
|
||||||
|
contract Block {
|
||||||
|
function timestamp() public view returns (uint ret);
|
||||||
|
|
||||||
|
function number() public view returns (uint ret);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
impl Contract {
|
impl Contract {
|
||||||
pub fn baseline() -> Self {
|
pub fn baseline() -> Self {
|
||||||
let code = include_str!("../contracts/Baseline.sol");
|
let code = include_str!("../contracts/Baseline.sol");
|
||||||
@@ -166,12 +174,34 @@ impl Contract {
|
|||||||
calldata: IERC20::totalSupplyCall::new(()).abi_encode(),
|
calldata: IERC20::totalSupplyCall::new(()).abi_encode(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn block_number() -> Self {
|
||||||
|
let code = include_str!("../contracts/Block.sol");
|
||||||
|
let name = "Block";
|
||||||
|
|
||||||
|
Self {
|
||||||
|
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||||
|
pvm_runtime: crate::compile_blob(name, code),
|
||||||
|
calldata: Block::numberCall::new(()).abi_encode(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_timestamp() -> Self {
|
||||||
|
let code = include_str!("../contracts/Block.sol");
|
||||||
|
let name = "Block";
|
||||||
|
|
||||||
|
Self {
|
||||||
|
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||||
|
pvm_runtime: crate::compile_blob(name, code),
|
||||||
|
calldata: Block::timestampCall::new(()).abi_encode(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use serde::{de::Deserialize, Serialize};
|
use serde::{de::Deserialize, Serialize};
|
||||||
use std::{collections::HashMap, fs::File};
|
use std::{collections::BTreeMap, fs::File};
|
||||||
|
|
||||||
use super::Contract;
|
use super::Contract;
|
||||||
|
|
||||||
@@ -181,14 +211,14 @@ mod tests {
|
|||||||
|
|
||||||
let existing = File::open(path)
|
let existing = File::open(path)
|
||||||
.map(|file| {
|
.map(|file| {
|
||||||
HashMap::<String, usize>::deserialize(&mut serde_json::Deserializer::from_reader(
|
BTreeMap::<String, usize>::deserialize(&mut serde_json::Deserializer::from_reader(
|
||||||
file,
|
file,
|
||||||
))
|
))
|
||||||
.expect("should be able to deserialze codesize data")
|
.expect("should be able to deserialze codesize data")
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
let sizes = HashMap::from([
|
let sizes = BTreeMap::from([
|
||||||
("Baseline", Contract::baseline().pvm_runtime.len()),
|
("Baseline", Contract::baseline().pvm_runtime.len()),
|
||||||
("Flipper", Contract::flipper().pvm_runtime.len()),
|
("Flipper", Contract::flipper().pvm_runtime.len()),
|
||||||
("Computation", Contract::odd_product(0).pvm_runtime.len()),
|
("Computation", Contract::odd_product(0).pvm_runtime.len()),
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ impl Default for CallOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
pub const BLOCK_NUMBER: u64 = 123;
|
||||||
|
pub const BLOCK_TIMESTAMP: u64 = 456;
|
||||||
|
|
||||||
pub fn new(input: Vec<u8>) -> Self {
|
pub fn new(input: Vec<u8>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
input,
|
input,
|
||||||
@@ -88,8 +91,12 @@ fn link_host_functions(engine: &Engine) -> Linker<State> {
|
|||||||
|caller: Caller<State>, out_ptr: u32, out_len_ptr: u32| -> Result<(), Trap> {
|
|caller: Caller<State>, out_ptr: u32, out_len_ptr: u32| -> Result<(), Trap> {
|
||||||
let (mut caller, state) = caller.split();
|
let (mut caller, state) = caller.split();
|
||||||
|
|
||||||
let out_len = caller.read_u32(out_len_ptr)?;
|
let out_len = caller.read_u32(out_len_ptr)? as usize;
|
||||||
assert_eq!(out_len, 16, "spurious output buffer size: {out_len}");
|
assert_eq!(
|
||||||
|
out_len,
|
||||||
|
revive_common::BYTE_LENGTH_VALUE,
|
||||||
|
"spurious output buffer size: {out_len}"
|
||||||
|
);
|
||||||
|
|
||||||
let value = state.value.to_le_bytes();
|
let value = state.value.to_le_bytes();
|
||||||
|
|
||||||
@@ -126,8 +133,16 @@ fn link_host_functions(engine: &Engine) -> Linker<State> {
|
|||||||
-> Result<u32, Trap> {
|
-> Result<u32, Trap> {
|
||||||
let (caller, state) = caller.split();
|
let (caller, state) = caller.split();
|
||||||
|
|
||||||
assert_eq!(key_len, 32, "storage key must be 32 bytes");
|
assert_eq!(
|
||||||
assert_eq!(value_len, 32, "storage value must be 32 bytes");
|
key_len as usize,
|
||||||
|
revive_common::BYTE_LENGTH_WORD,
|
||||||
|
"storage key must be 32 bytes"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
value_len as usize,
|
||||||
|
revive_common::BYTE_LENGTH_WORD,
|
||||||
|
"storage value must be 32 bytes"
|
||||||
|
);
|
||||||
|
|
||||||
let key = caller.read_memory_into_vec(key_ptr, key_len)?;
|
let key = caller.read_memory_into_vec(key_ptr, key_len)?;
|
||||||
let value = caller.read_memory_into_vec(value_ptr, value_len)?;
|
let value = caller.read_memory_into_vec(value_ptr, value_len)?;
|
||||||
@@ -154,8 +169,12 @@ fn link_host_functions(engine: &Engine) -> Linker<State> {
|
|||||||
let (mut caller, state) = caller.split();
|
let (mut caller, state) = caller.split();
|
||||||
|
|
||||||
let key = caller.read_memory_into_vec(key_ptr, key_len)?;
|
let key = caller.read_memory_into_vec(key_ptr, key_len)?;
|
||||||
let out_len = caller.read_u32(out_len_ptr)?;
|
let out_len = caller.read_u32(out_len_ptr)? as usize;
|
||||||
assert_eq!(out_len, 32, "spurious output buffer size: {out_len}");
|
assert_eq!(
|
||||||
|
out_len,
|
||||||
|
revive_common::BYTE_LENGTH_WORD,
|
||||||
|
"spurious output buffer size: {out_len}"
|
||||||
|
);
|
||||||
|
|
||||||
let value = state
|
let value = state
|
||||||
.storage
|
.storage
|
||||||
@@ -192,6 +211,48 @@ fn link_host_functions(engine: &Engine) -> Linker<State> {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
linker
|
||||||
|
.func_wrap(
|
||||||
|
runtime_api::NOW,
|
||||||
|
|caller: Caller<State>, out_ptr: u32, out_len_ptr: u32| {
|
||||||
|
let (mut caller, _) = caller.split();
|
||||||
|
|
||||||
|
let out_len = caller.read_u32(out_len_ptr)? as usize;
|
||||||
|
assert_eq!(
|
||||||
|
out_len,
|
||||||
|
revive_common::BYTE_LENGTH_BLOCK_TIMESTAMP,
|
||||||
|
"spurious output buffer size: {out_len}"
|
||||||
|
);
|
||||||
|
|
||||||
|
caller.write_memory(out_ptr, &State::BLOCK_TIMESTAMP.to_le_bytes())?;
|
||||||
|
caller.write_memory(out_len_ptr, &64u32.to_le_bytes())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
linker
|
||||||
|
.func_wrap(
|
||||||
|
runtime_api::BLOCK_NUMBER,
|
||||||
|
|caller: Caller<State>, out_ptr: u32, out_len_ptr: u32| {
|
||||||
|
let (mut caller, _) = caller.split();
|
||||||
|
|
||||||
|
let out_len = caller.read_u32(out_len_ptr)? as usize;
|
||||||
|
assert_eq!(
|
||||||
|
out_len,
|
||||||
|
revive_common::BYTE_LENGTH_BLOCK_NUMBER,
|
||||||
|
"spurious output buffer size: {out_len}"
|
||||||
|
);
|
||||||
|
|
||||||
|
caller.write_memory(out_ptr, &State::BLOCK_NUMBER.to_le_bytes())?;
|
||||||
|
caller.write_memory(out_len_ptr, &64u32.to_le_bytes())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
linker
|
linker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ fn transferred_value() {
|
|||||||
);
|
);
|
||||||
let code = crate::compile_blob("Value", include_str!("../contracts/Value.sol"));
|
let code = crate::compile_blob("Value", include_str!("../contracts/Value.sol"));
|
||||||
let mut state = State::new(Value::valueCall::SELECTOR.to_vec());
|
let mut state = State::new(Value::valueCall::SELECTOR.to_vec());
|
||||||
state.value = 0x1;
|
state.value = 123;
|
||||||
|
|
||||||
let (mut instance, export) = mock_runtime::prepare(&code, None);
|
let (mut instance, export) = mock_runtime::prepare(&code, None);
|
||||||
let state = crate::mock_runtime::call(state, &mut instance, export);
|
let state = crate::mock_runtime::call(state, &mut instance, export);
|
||||||
@@ -264,3 +264,19 @@ fn sha1() {
|
|||||||
let received = FixedBytes::<20>::from_slice(&state.output.data[..20]);
|
let received = FixedBytes::<20>::from_slice(&state.output.data[..20]);
|
||||||
assert_eq!(received, expected);
|
assert_eq!(received, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn block_number() {
|
||||||
|
let state = assert_success(Contract::block_number(), true);
|
||||||
|
let received = U256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
|
||||||
|
let expected = U256::from(mock_runtime::State::BLOCK_NUMBER);
|
||||||
|
assert_eq!(received, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn block_timestamp() {
|
||||||
|
let state = assert_success(Contract::block_timestamp(), true);
|
||||||
|
let received = U256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
|
||||||
|
let expected = U256::from(mock_runtime::State::BLOCK_TIMESTAMP);
|
||||||
|
assert_eq!(received, expected);
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,7 +43,13 @@ pub static GLOBAL_CONST_ARRAY_PREFIX: &str = "const_array_";
|
|||||||
pub static GLOBAL_VERBATIM_GETTER_PREFIX: &str = "get_global::";
|
pub static GLOBAL_VERBATIM_GETTER_PREFIX: &str = "get_global::";
|
||||||
|
|
||||||
/// The static word size.
|
/// The static word size.
|
||||||
pub static GLOBAL_WORD_SIZE: &str = "word_size";
|
pub static GLOBAL_I256_SIZE: &str = "i256_size";
|
||||||
|
|
||||||
|
/// The static value size.
|
||||||
|
pub static GLOBAL_I128_SIZE: &str = "i128_size";
|
||||||
|
|
||||||
|
/// The static i64 size.
|
||||||
|
pub static GLOBAL_I64_SIZE: &str = "i64_size";
|
||||||
|
|
||||||
/// The external call data offset in the auxiliary heap.
|
/// The external call data offset in the auxiliary heap.
|
||||||
pub const HEAP_AUX_OFFSET_EXTERNAL_CALL: u64 = 0;
|
pub const HEAP_AUX_OFFSET_EXTERNAL_CALL: u64 = 0;
|
||||||
|
|||||||
@@ -10,12 +10,16 @@ pub static DEPLOY: &str = "deploy";
|
|||||||
/// Useful for configuring common attributes and linkage.
|
/// Useful for configuring common attributes and linkage.
|
||||||
pub static EXPORTS: [&str; 2] = [CALL, DEPLOY];
|
pub static EXPORTS: [&str; 2] = [CALL, DEPLOY];
|
||||||
|
|
||||||
|
pub static BLOCK_NUMBER: &str = "block_number";
|
||||||
|
|
||||||
pub static GET_STORAGE: &str = "get_storage";
|
pub static GET_STORAGE: &str = "get_storage";
|
||||||
|
|
||||||
pub static HASH_KECCAK_256: &str = "hash_keccak_256";
|
pub static HASH_KECCAK_256: &str = "hash_keccak_256";
|
||||||
|
|
||||||
pub static INPUT: &str = "input";
|
pub static INPUT: &str = "input";
|
||||||
|
|
||||||
|
pub static NOW: &str = "now";
|
||||||
|
|
||||||
pub static RETURN: &str = "seal_return";
|
pub static RETURN: &str = "seal_return";
|
||||||
|
|
||||||
pub static SET_STORAGE: &str = "set_storage";
|
pub static SET_STORAGE: &str = "set_storage";
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ impl<'ctx> Intrinsics<'ctx> {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
let void_type = llvm.void_type();
|
let void_type = llvm.void_type();
|
||||||
let bool_type = llvm.bool_type();
|
let bool_type = llvm.bool_type();
|
||||||
let field_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32);
|
let word_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32);
|
||||||
let _stack_field_pointer_type = llvm.ptr_type(AddressSpace::Stack.into());
|
let _stack_field_pointer_type = llvm.ptr_type(AddressSpace::Stack.into());
|
||||||
let heap_field_pointer_type = llvm.ptr_type(AddressSpace::Heap.into());
|
let heap_field_pointer_type = llvm.ptr_type(AddressSpace::Heap.into());
|
||||||
let generic_byte_pointer_type = llvm.ptr_type(AddressSpace::Generic.into());
|
let generic_byte_pointer_type = llvm.ptr_type(AddressSpace::Generic.into());
|
||||||
@@ -58,7 +58,7 @@ impl<'ctx> Intrinsics<'ctx> {
|
|||||||
&[
|
&[
|
||||||
heap_field_pointer_type.as_basic_type_enum().into(),
|
heap_field_pointer_type.as_basic_type_enum().into(),
|
||||||
heap_field_pointer_type.as_basic_type_enum().into(),
|
heap_field_pointer_type.as_basic_type_enum().into(),
|
||||||
field_type.as_basic_type_enum().into(),
|
word_type.as_basic_type_enum().into(),
|
||||||
bool_type.as_basic_type_enum().into(),
|
bool_type.as_basic_type_enum().into(),
|
||||||
],
|
],
|
||||||
false,
|
false,
|
||||||
@@ -72,7 +72,7 @@ impl<'ctx> Intrinsics<'ctx> {
|
|||||||
&[
|
&[
|
||||||
heap_field_pointer_type.as_basic_type_enum().into(),
|
heap_field_pointer_type.as_basic_type_enum().into(),
|
||||||
generic_byte_pointer_type.as_basic_type_enum().into(),
|
generic_byte_pointer_type.as_basic_type_enum().into(),
|
||||||
field_type.as_basic_type_enum().into(),
|
word_type.as_basic_type_enum().into(),
|
||||||
bool_type.as_basic_type_enum().into(),
|
bool_type.as_basic_type_enum().into(),
|
||||||
],
|
],
|
||||||
false,
|
false,
|
||||||
@@ -82,7 +82,7 @@ impl<'ctx> Intrinsics<'ctx> {
|
|||||||
llvm,
|
llvm,
|
||||||
module,
|
module,
|
||||||
Self::FUNCTION_BYTE_SWAP,
|
Self::FUNCTION_BYTE_SWAP,
|
||||||
field_type.fn_type(&[field_type.as_basic_type_enum().into()], false),
|
word_type.fn_type(&[word_type.as_basic_type_enum().into()], false),
|
||||||
);
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@@ -114,7 +114,7 @@ impl<'ctx> Intrinsics<'ctx> {
|
|||||||
llvm: &'ctx inkwell::context::Context,
|
llvm: &'ctx inkwell::context::Context,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> Vec<inkwell::types::BasicTypeEnum<'ctx>> {
|
) -> Vec<inkwell::types::BasicTypeEnum<'ctx>> {
|
||||||
let field_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32);
|
let word_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32);
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
name if name == Self::FUNCTION_MEMORY_COPY => vec![
|
name if name == Self::FUNCTION_MEMORY_COPY => vec![
|
||||||
@@ -122,16 +122,16 @@ impl<'ctx> Intrinsics<'ctx> {
|
|||||||
.as_basic_type_enum(),
|
.as_basic_type_enum(),
|
||||||
llvm.ptr_type(AddressSpace::Heap.into())
|
llvm.ptr_type(AddressSpace::Heap.into())
|
||||||
.as_basic_type_enum(),
|
.as_basic_type_enum(),
|
||||||
field_type.as_basic_type_enum(),
|
word_type.as_basic_type_enum(),
|
||||||
],
|
],
|
||||||
name if name == Self::FUNCTION_MEMORY_COPY_FROM_GENERIC => vec![
|
name if name == Self::FUNCTION_MEMORY_COPY_FROM_GENERIC => vec![
|
||||||
llvm.ptr_type(AddressSpace::Heap.into())
|
llvm.ptr_type(AddressSpace::Heap.into())
|
||||||
.as_basic_type_enum(),
|
.as_basic_type_enum(),
|
||||||
llvm.ptr_type(AddressSpace::Generic.into())
|
llvm.ptr_type(AddressSpace::Generic.into())
|
||||||
.as_basic_type_enum(),
|
.as_basic_type_enum(),
|
||||||
field_type.as_basic_type_enum(),
|
word_type.as_basic_type_enum(),
|
||||||
],
|
],
|
||||||
name if name == Self::FUNCTION_BYTE_SWAP => vec![field_type.as_basic_type_enum()],
|
name if name == Self::FUNCTION_BYTE_SWAP => vec![word_type.as_basic_type_enum()],
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,10 +85,30 @@ impl Entry {
|
|||||||
);
|
);
|
||||||
|
|
||||||
context.set_global(
|
context.set_global(
|
||||||
crate::polkavm::GLOBAL_WORD_SIZE,
|
crate::polkavm::GLOBAL_I256_SIZE,
|
||||||
context.xlen_type(),
|
context.xlen_type(),
|
||||||
AddressSpace::Stack,
|
AddressSpace::Stack,
|
||||||
context.integer_const(crate::polkavm::XLEN, revive_common::BYTE_LENGTH_WORD as u64),
|
context.integer_const(
|
||||||
|
crate::polkavm::XLEN,
|
||||||
|
revive_common::BYTE_LENGTH_X64 as u64 * 4,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
context.set_global(
|
||||||
|
crate::polkavm::GLOBAL_I128_SIZE,
|
||||||
|
context.xlen_type(),
|
||||||
|
AddressSpace::Stack,
|
||||||
|
context.integer_const(
|
||||||
|
crate::polkavm::XLEN,
|
||||||
|
revive_common::BYTE_LENGTH_X64 as u64 * 2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
context.set_global(
|
||||||
|
crate::polkavm::GLOBAL_I64_SIZE,
|
||||||
|
context.xlen_type(),
|
||||||
|
AddressSpace::Stack,
|
||||||
|
context.integer_const(crate::polkavm::XLEN, revive_common::BYTE_LENGTH_X64 as u64),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -610,16 +610,45 @@ where
|
|||||||
Pointer::new(r#type, AddressSpace::Stack, pointer)
|
Pointer::new(r#type, AddressSpace::Stack, pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates a word-sized byte buffer on the stack.
|
/// Allocate an int of size `bit_length` on the stack.
|
||||||
/// Returns the allocation pointer and the length pointer.
|
/// Returns the allocation pointer and the length pointer.
|
||||||
pub fn build_stack_word_parameter(&self, name: &str) -> (Pointer<'ctx>, Pointer<'ctx>) {
|
///
|
||||||
let buffer_pointer = self.build_alloca(self.word_type(), name);
|
/// Useful helper for passing runtime API parameters on the stack.
|
||||||
let length_pointer = self
|
pub fn build_stack_parameter(
|
||||||
.get_global(GLOBAL_WORD_SIZE)
|
&self,
|
||||||
.expect("should be declared");
|
bit_length: usize,
|
||||||
|
name: &str,
|
||||||
|
) -> (Pointer<'ctx>, Pointer<'ctx>) {
|
||||||
|
let buffer_pointer = self.build_alloca(self.integer_type(bit_length), name);
|
||||||
|
let symbol = match bit_length {
|
||||||
|
256 => GLOBAL_I256_SIZE,
|
||||||
|
128 => GLOBAL_I128_SIZE,
|
||||||
|
64 => GLOBAL_I64_SIZE,
|
||||||
|
_ => unreachable!("invalid stack parameter bit width: {bit_length}"),
|
||||||
|
};
|
||||||
|
let length_pointer = self.get_global(symbol).expect("should be declared");
|
||||||
(buffer_pointer, length_pointer.into())
|
(buffer_pointer, length_pointer.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load the integer at given pointer and zero extend it to the VM word size.
|
||||||
|
pub fn build_load_word(
|
||||||
|
&self,
|
||||||
|
pointer: Pointer<'ctx>,
|
||||||
|
bit_length: usize,
|
||||||
|
name: &str,
|
||||||
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
|
||||||
|
let value = self.build_load(
|
||||||
|
pointer.cast(self.integer_type(bit_length)),
|
||||||
|
&format!("load_{name}"),
|
||||||
|
)?;
|
||||||
|
let value_extended = self.builder().build_int_z_extend(
|
||||||
|
value.into_int_value(),
|
||||||
|
self.word_type(),
|
||||||
|
&format!("zext_{name}"),
|
||||||
|
)?;
|
||||||
|
Ok(value_extended.as_basic_value_enum())
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds a stack load instruction.
|
/// Builds a stack load instruction.
|
||||||
/// Sets the alignment to 256 bits for the stack and 1 bit for the heap, parent, and child.
|
/// Sets the alignment to 256 bits for the stack and 1 bit for the heap, parent, and child.
|
||||||
pub fn build_load(
|
pub fn build_load(
|
||||||
@@ -651,7 +680,7 @@ where
|
|||||||
.set_alignment(revive_common::BYTE_LENGTH_BYTE as u32)
|
.set_alignment(revive_common::BYTE_LENGTH_BYTE as u32)
|
||||||
.expect("Alignment is valid");
|
.expect("Alignment is valid");
|
||||||
|
|
||||||
Ok(self.build_byte_swap(value))
|
self.build_byte_swap(value)
|
||||||
}
|
}
|
||||||
AddressSpace::TransientStorage => todo!(),
|
AddressSpace::TransientStorage => todo!(),
|
||||||
AddressSpace::Storage => {
|
AddressSpace::Storage => {
|
||||||
@@ -670,8 +699,8 @@ where
|
|||||||
self.builder()
|
self.builder()
|
||||||
.build_store(storage_key_pointer.value, storage_key_value)?;
|
.build_store(storage_key_pointer.value, storage_key_value)?;
|
||||||
|
|
||||||
let (storage_value_pointer, storage_value_length_pointer) =
|
let (storage_value_pointer, storage_value_length_pointer) = self
|
||||||
self.build_stack_word_parameter("storage_value_pointer");
|
.build_stack_parameter(revive_common::BIT_LENGTH_WORD, "storage_value_pointer");
|
||||||
|
|
||||||
self.build_runtime_call(
|
self.build_runtime_call(
|
||||||
runtime_api::GET_STORAGE,
|
runtime_api::GET_STORAGE,
|
||||||
@@ -688,13 +717,13 @@ where
|
|||||||
// If a key doesn't exist the "zero" value is returned.
|
// If a key doesn't exist the "zero" value is returned.
|
||||||
|
|
||||||
self.build_load(storage_value_pointer, "storage_value_load")
|
self.build_load(storage_value_pointer, "storage_value_load")
|
||||||
.map(|value| self.build_byte_swap(value))
|
.and_then(|value| self.build_byte_swap(value))
|
||||||
}
|
}
|
||||||
AddressSpace::Code | AddressSpace::HeapAuxiliary => todo!(),
|
AddressSpace::Code | AddressSpace::HeapAuxiliary => todo!(),
|
||||||
AddressSpace::Generic => Ok(self.build_byte_swap(self.build_load(
|
AddressSpace::Generic => Ok(self.build_byte_swap(self.build_load(
|
||||||
pointer.address_space_cast(self, AddressSpace::Stack, &format!("{}_cast", name))?,
|
pointer.address_space_cast(self, AddressSpace::Stack, &format!("{}_cast", name))?,
|
||||||
name,
|
name,
|
||||||
)?)),
|
)?)?),
|
||||||
AddressSpace::Stack => {
|
AddressSpace::Stack => {
|
||||||
let value = self
|
let value = self
|
||||||
.builder()
|
.builder()
|
||||||
@@ -735,7 +764,7 @@ where
|
|||||||
|
|
||||||
let value = value.as_basic_value_enum();
|
let value = value.as_basic_value_enum();
|
||||||
let value = match value.get_type().into_int_type().get_bit_width() as usize {
|
let value = match value.get_type().into_int_type().get_bit_width() as usize {
|
||||||
revive_common::BIT_LENGTH_WORD => self.build_byte_swap(value),
|
revive_common::BIT_LENGTH_WORD => self.build_byte_swap(value)?,
|
||||||
revive_common::BIT_LENGTH_BYTE => value,
|
revive_common::BIT_LENGTH_BYTE => value,
|
||||||
_ => unreachable!("Only word and byte sized values can be stored on EVM heap"),
|
_ => unreachable!("Only word and byte sized values can be stored on EVM heap"),
|
||||||
};
|
};
|
||||||
@@ -756,7 +785,7 @@ where
|
|||||||
self.build_alloca(storage_key_value.get_type(), "storage_key");
|
self.build_alloca(storage_key_value.get_type(), "storage_key");
|
||||||
|
|
||||||
let storage_value_value = self
|
let storage_value_value = self
|
||||||
.build_byte_swap(value.as_basic_value_enum())
|
.build_byte_swap(value.as_basic_value_enum())?
|
||||||
.into_int_value();
|
.into_int_value();
|
||||||
let storage_value_pointer =
|
let storage_value_pointer =
|
||||||
self.build_alloca(storage_value_value.get_type(), "storage_value");
|
self.build_alloca(storage_value_value.get_type(), "storage_value");
|
||||||
@@ -790,7 +819,7 @@ where
|
|||||||
AddressSpace::Code | AddressSpace::HeapAuxiliary => {}
|
AddressSpace::Code | AddressSpace::HeapAuxiliary => {}
|
||||||
AddressSpace::Generic => self.build_store(
|
AddressSpace::Generic => self.build_store(
|
||||||
pointer.address_space_cast(self, AddressSpace::Stack, "cast")?,
|
pointer.address_space_cast(self, AddressSpace::Stack, "cast")?,
|
||||||
self.build_byte_swap(value.as_basic_value_enum()),
|
self.build_byte_swap(value.as_basic_value_enum())?,
|
||||||
)?,
|
)?,
|
||||||
AddressSpace::Stack => {
|
AddressSpace::Stack => {
|
||||||
let instruction = self.builder.build_store(pointer.value, value).unwrap();
|
let instruction = self.builder.build_store(pointer.value, value).unwrap();
|
||||||
@@ -807,9 +836,17 @@ where
|
|||||||
pub fn build_byte_swap(
|
pub fn build_byte_swap(
|
||||||
&self,
|
&self,
|
||||||
value: inkwell::values::BasicValueEnum<'ctx>,
|
value: inkwell::values::BasicValueEnum<'ctx>,
|
||||||
) -> inkwell::values::BasicValueEnum<'ctx> {
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
|
||||||
self.build_call(self.intrinsics().byte_swap, &[value], "call_byte_swap")
|
Ok(self
|
||||||
.expect("byte_swap should return a value")
|
.builder()
|
||||||
|
.build_call(
|
||||||
|
self.intrinsics().byte_swap.value,
|
||||||
|
&[value.into()],
|
||||||
|
"call_byte_swap",
|
||||||
|
)?
|
||||||
|
.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a GEP instruction.
|
/// Builds a GEP instruction.
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ where
|
|||||||
);
|
);
|
||||||
context
|
context
|
||||||
.build_load(offset, "calldata_value")
|
.build_load(offset, "calldata_value")
|
||||||
.map(|value| context.build_byte_swap(value))
|
.and_then(|value| context.build_byte_swap(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the calldata size.
|
/// Translates the calldata size.
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use inkwell::values::BasicValue;
|
|||||||
|
|
||||||
use crate::polkavm::context::Context;
|
use crate::polkavm::context::Context;
|
||||||
use crate::polkavm::Dependency;
|
use crate::polkavm::Dependency;
|
||||||
|
use crate::polkavm_const::runtime_api;
|
||||||
|
|
||||||
/// Translates the `gas_limit` instruction.
|
/// Translates the `gas_limit` instruction.
|
||||||
pub fn gas_limit<'ctx, D>(
|
pub fn gas_limit<'ctx, D>(
|
||||||
@@ -47,22 +48,52 @@ where
|
|||||||
|
|
||||||
/// Translates the `block_number` instruction.
|
/// Translates the `block_number` instruction.
|
||||||
pub fn block_number<'ctx, D>(
|
pub fn block_number<'ctx, D>(
|
||||||
_context: &mut Context<'ctx, D>,
|
context: &mut Context<'ctx, D>,
|
||||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
todo!()
|
let (output_pointer, output_length_pointer) = context.build_stack_parameter(
|
||||||
|
revive_common::BIT_LENGTH_BLOCK_NUMBER,
|
||||||
|
"block_timestamp_output",
|
||||||
|
);
|
||||||
|
context.build_runtime_call(
|
||||||
|
runtime_api::BLOCK_NUMBER,
|
||||||
|
&[
|
||||||
|
output_pointer.to_int(context).into(),
|
||||||
|
output_length_pointer.to_int(context).into(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
context.build_load_word(
|
||||||
|
output_pointer,
|
||||||
|
revive_common::BIT_LENGTH_BLOCK_NUMBER,
|
||||||
|
"block_number",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the `block_timestamp` instruction.
|
/// Translates the `block_timestamp` instruction.
|
||||||
pub fn block_timestamp<'ctx, D>(
|
pub fn block_timestamp<'ctx, D>(
|
||||||
_context: &mut Context<'ctx, D>,
|
context: &mut Context<'ctx, D>,
|
||||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
todo!()
|
let (output_pointer, output_length_pointer) = context.build_stack_parameter(
|
||||||
|
revive_common::BIT_LENGTH_BLOCK_TIMESTAMP,
|
||||||
|
"block_timestamp_output",
|
||||||
|
);
|
||||||
|
context.build_runtime_call(
|
||||||
|
runtime_api::NOW,
|
||||||
|
&[
|
||||||
|
output_pointer.to_int(context).into(),
|
||||||
|
output_length_pointer.to_int(context).into(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
context.build_load_word(
|
||||||
|
output_pointer,
|
||||||
|
revive_common::BIT_LENGTH_BLOCK_TIMESTAMP,
|
||||||
|
"block_timestamp",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the `block_hash` instruction.
|
/// Translates the `block_hash` instruction.
|
||||||
|
|||||||
@@ -27,5 +27,5 @@ where
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(context.build_byte_swap(context.build_load(output_pointer, "sha3_output")?))
|
context.build_byte_swap(context.build_load(output_pointer, "sha3_output")?)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,42 +23,20 @@ pub fn value<'ctx, D>(
|
|||||||
where
|
where
|
||||||
D: Dependency + Clone,
|
D: Dependency + Clone,
|
||||||
{
|
{
|
||||||
let output_pointer = context.build_alloca(context.value_type(), "output_pointer");
|
let (output_pointer, output_length_pointer) =
|
||||||
let output_pointer_casted = context.builder().build_ptr_to_int(
|
context.build_stack_parameter(revive_common::BIT_LENGTH_VALUE, "value_transferred_output");
|
||||||
output_pointer.value,
|
|
||||||
context.xlen_type(),
|
|
||||||
"output_pointer_casted",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let output_length_pointer = context.build_alloca(context.xlen_type(), "output_len_pointer");
|
|
||||||
let output_length_pointer_casted = context.builder().build_ptr_to_int(
|
|
||||||
output_length_pointer.value,
|
|
||||||
context.xlen_type(),
|
|
||||||
"output_pointer_casted",
|
|
||||||
)?;
|
|
||||||
context.build_store(
|
|
||||||
output_length_pointer,
|
|
||||||
context.integer_const(
|
|
||||||
crate::polkavm::XLEN,
|
|
||||||
revive_common::BYTE_LENGTH_VALUE as u64,
|
|
||||||
),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
context.build_runtime_call(
|
context.build_runtime_call(
|
||||||
runtime_api::VALUE_TRANSFERRED,
|
runtime_api::VALUE_TRANSFERRED,
|
||||||
&[
|
&[
|
||||||
output_pointer_casted.into(),
|
output_pointer.to_int(context).into(),
|
||||||
output_length_pointer_casted.into(),
|
output_length_pointer.to_int(context).into(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
context.build_load_word(
|
||||||
let value = context.build_load(output_pointer, "transferred_value")?;
|
output_pointer,
|
||||||
let value_extended = context.builder().build_int_z_extend(
|
revive_common::BIT_LENGTH_VALUE,
|
||||||
value.into_int_value(),
|
"value_transferred",
|
||||||
context.word_type(),
|
)
|
||||||
"transferred_value_extended",
|
|
||||||
)?;
|
|
||||||
Ok(value_extended.as_basic_value_enum())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the `balance` instructions.
|
/// Translates the `balance` instructions.
|
||||||
|
|||||||
Reference in New Issue
Block a user