implement block.number and block.timestamp

Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
xermicus
2024-05-07 18:03:17 +02:00
parent a7318f2ef6
commit 95ff85c6d1
20 changed files with 295 additions and 83 deletions
+1
View File
@@ -15,6 +15,7 @@ env_logger = { workspace = true }
revive-solidity = { path = "../solidity" }
revive-differential = { path = "../differential" }
revive-llvm-context = { path = "../llvm-context" }
revive-common = { path = "../common" }
[dev-dependencies]
sha1 = { workspace = true }
+5 -5
View File
@@ -1,7 +1,7 @@
{
"ERC20": 53171,
"Baseline": 3912,
"Flipper": 4353,
"Fibonacci": 5971,
"Computation": 7380
"Baseline": 3917,
"Computation": 7363,
"ERC20": 52714,
"Fibonacci": 5965,
"Flipper": 4336
}
+13
View File
@@ -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;
}
}
+33 -3
View File
@@ -67,6 +67,14 @@ sol!(
}
);
sol!(
contract Block {
function timestamp() public view returns (uint ret);
function number() public view returns (uint ret);
}
);
impl Contract {
pub fn baseline() -> Self {
let code = include_str!("../contracts/Baseline.sol");
@@ -166,12 +174,34 @@ impl Contract {
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)]
mod tests {
use serde::{de::Deserialize, Serialize};
use std::{collections::HashMap, fs::File};
use std::{collections::BTreeMap, fs::File};
use super::Contract;
@@ -181,14 +211,14 @@ mod tests {
let existing = File::open(path)
.map(|file| {
HashMap::<String, usize>::deserialize(&mut serde_json::Deserializer::from_reader(
BTreeMap::<String, usize>::deserialize(&mut serde_json::Deserializer::from_reader(
file,
))
.expect("should be able to deserialze codesize data")
})
.ok();
let sizes = HashMap::from([
let sizes = BTreeMap::from([
("Baseline", Contract::baseline().pvm_runtime.len()),
("Flipper", Contract::flipper().pvm_runtime.len()),
("Computation", Contract::odd_product(0).pvm_runtime.len()),
+67 -6
View File
@@ -33,6 +33,9 @@ impl Default for CallOutput {
}
impl State {
pub const BLOCK_NUMBER: u64 = 123;
pub const BLOCK_TIMESTAMP: u64 = 456;
pub fn new(input: Vec<u8>) -> Self {
Self {
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> {
let (mut caller, state) = caller.split();
let out_len = caller.read_u32(out_len_ptr)?;
assert_eq!(out_len, 16, "spurious output buffer size: {out_len}");
let out_len = caller.read_u32(out_len_ptr)? as usize;
assert_eq!(
out_len,
revive_common::BYTE_LENGTH_VALUE,
"spurious output buffer size: {out_len}"
);
let value = state.value.to_le_bytes();
@@ -126,8 +133,16 @@ fn link_host_functions(engine: &Engine) -> Linker<State> {
-> Result<u32, Trap> {
let (caller, state) = caller.split();
assert_eq!(key_len, 32, "storage key must be 32 bytes");
assert_eq!(value_len, 32, "storage value must be 32 bytes");
assert_eq!(
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 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 key = caller.read_memory_into_vec(key_ptr, key_len)?;
let out_len = caller.read_u32(out_len_ptr)?;
assert_eq!(out_len, 32, "spurious output buffer size: {out_len}");
let out_len = caller.read_u32(out_len_ptr)? as usize;
assert_eq!(
out_len,
revive_common::BYTE_LENGTH_WORD,
"spurious output buffer size: {out_len}"
);
let value = state
.storage
@@ -192,6 +211,48 @@ fn link_host_functions(engine: &Engine) -> Linker<State> {
)
.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
}
+17 -1
View File
@@ -126,7 +126,7 @@ fn transferred_value() {
);
let code = crate::compile_blob("Value", include_str!("../contracts/Value.sol"));
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 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]);
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);
}