Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(levm): fix revert cases for contracts to run correctly #1090

Open
wants to merge 73 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 70 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
3492657
db, warm addresses and cache scaffold
JereSalo Oct 26, 2024
13d7d47
ongoing changes on db
JereSalo Oct 28, 2024
5fd1345
Add `Database` api
juanimedone Oct 28, 2024
c0f8cf8
some changes in Database and Db
JereSalo Oct 28, 2024
2e9cf34
add method get_from_db_then_cache()
JereSalo Oct 28, 2024
1ddefdf
fix some tests
JereSalo Oct 28, 2024
b5eebd4
Fix environment opcodes
juanimedone Oct 28, 2024
99b4fc3
make changes to vm testing function
JereSalo Oct 28, 2024
6b381a8
Merge branch 'levm/refactor/db' of github.com:lambdaclass/lambda_ethe…
JereSalo Oct 28, 2024
594ef74
fix more tests
JereSalo Oct 28, 2024
cdd786d
fix some issues in vm
JereSalo Oct 28, 2024
0b8614b
Add `get_block_hash`
juanimedone Oct 28, 2024
5c380f1
fix more tests i guess..
JereSalo Oct 28, 2024
db12190
Merge branch 'levm/refactor/db' of github.com:lambdaclass/lambda_ethe…
JereSalo Oct 28, 2024
ffad514
'fix' errors in call opcode
JereSalo Oct 28, 2024
61d36c8
change to create
JereSalo Oct 28, 2024
eaf4616
Fix `SLOAD` and `SSTORE`
juanimedone Oct 28, 2024
f34b493
changes in caching from db
JereSalo Oct 28, 2024
1fa59e0
Merge branch 'levm/refactor/db' of github.com:lambdaclass/lambda_ethe…
JereSalo Oct 28, 2024
c3b21ff
Fix lint
juanimedone Oct 28, 2024
b2fe679
fix errors in tests
JereSalo Oct 28, 2024
b1c9be0
Merge branch 'levm/refactor/db' of github.com:lambdaclass/lambda_ethe…
JereSalo Oct 28, 2024
96e66cc
cache all data added to db in tests
JereSalo Oct 28, 2024
e66aa3e
fix one test...
JereSalo Oct 28, 2024
741a543
fix call tests
JereSalo Oct 28, 2024
91ab731
fix sstore
JereSalo Oct 28, 2024
7c7ada9
fix create tests
JereSalo Oct 28, 2024
842a530
Fix `extcodehash_account_with_empty_code`
juanimedone Oct 28, 2024
965bc4c
Fix compiling warning
juanimedone Oct 28, 2024
439c2ac
change variables names for clarity
JereSalo Oct 29, 2024
abd5789
fix call opcodes, remove delegate and change opcodes that used it
JereSalo Oct 29, 2024
96a83c9
comment delegatecall tests for being messy :)
JereSalo Oct 29, 2024
63d67bc
add comments to callframe attributes
JereSalo Oct 29, 2024
08cc426
run cargo fmt
JereSalo Oct 29, 2024
dd64d70
remove unused object
JereSalo Oct 29, 2024
b463d30
remove commented methods from Db implementation of Database trait
JereSalo Oct 29, 2024
5233c57
delete debug trait for database and vm, for integration to work
JereSalo Oct 29, 2024
e37a47e
change storage key from U256 to H256
JereSalo Oct 29, 2024
9d7e700
change block_number from U256 to u64
JereSalo Oct 29, 2024
c42f9f0
run cargo fmt
JereSalo Oct 29, 2024
18868f6
Merge branch 'main' into levm/refactor/db
juanimedone Oct 29, 2024
84330a6
Merge fixes
juanimedone Oct 30, 2024
97a6e11
Improve call/create transactions handling
maximopalopoli Oct 30, 2024
8a49f1f
Move validations in new to transact
maximopalopoli Oct 30, 2024
f0440fc
Delete commented code
maximopalopoli Oct 30, 2024
55ab9eb
starting changes, doesnt work
maximopalopoli Oct 30, 2024
3cee7e5
environment changes
JereSalo Oct 30, 2024
c04a00f
Fix tests
jrchatruc Oct 30, 2024
fedefff
Fix mutable variable
jrchatruc Oct 30, 2024
4b5f0fc
[WIP] Levm ethereum Rust integration
jrchatruc Oct 30, 2024
167a04e
fix tests and lints
JereSalo Oct 30, 2024
b85bff9
change name of package ethereum_rust_levm
JereSalo Oct 31, 2024
b5dae80
change storewrapper implementation of database methods
JereSalo Oct 31, 2024
5cc1adc
[WIP] More progress
jrchatruc Oct 31, 2024
a979da3
add gas consumed by sstore (without gas refunds)
JereSalo Oct 31, 2024
376248f
fix gas cost issue, now call to a contract works properly
JereSalo Oct 31, 2024
63550e1
fix sstore test
JereSalo Nov 1, 2024
bce322a
Fix clippy alerts and fmt
maximopalopoli Nov 1, 2024
557e57a
Change address obtaining to prevent overflow + fmt
maximopalopoli Nov 1, 2024
d73f18f
add some gas comnsumption comments
JereSalo Nov 4, 2024
5bd6be8
feat(levm): merge `main` into `levm-ethereum-rust-integration` (#1062)
ilitteri Nov 4, 2024
7993506
Merge branch 'main' of github.com:lambdaclass/lambda_ethereum_rust in…
ilitteri Nov 4, 2024
30d447d
fix(levm): fix generic_call (#1063)
JereSalo Nov 4, 2024
b065162
feat(levm): prepare levm-ethereum-rust-integration for merge (#1064)
ilitteri Nov 5, 2024
d2987a8
Fix default execute_block
ilitteri Nov 5, 2024
28f45f9
Add conditional compilation on levm's db
ilitteri Nov 5, 2024
29d56b8
Make ethereum_rust_levm optional
ilitteri Nov 5, 2024
b85cbde
Merge branch 'main' of github.com:lambdaclass/lambda_ethereum_rust in…
ilitteri Nov 5, 2024
ab9ef19
fix gas consumption in report
JereSalo Nov 6, 2024
0a944fb
fix balance substraction when contract reverts
JereSalo Nov 6, 2024
016b0b4
Update crates/vm/levm/src/errors.rs
JereSalo Nov 6, 2024
b10fa27
nits
JereSalo Nov 6, 2024
2eec80a
run cargo fmt
JereSalo Nov 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/ethereum_rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ path = "./ethereum_rust.rs"
default = []
dev = ["dep:ethereum_rust-dev"]
l2 = ["ethereum_rust-vm/l2"]
levm = ["ethereum_rust-vm/levm", "ethereum_rust-blockchain/levm"]
2 changes: 2 additions & 0 deletions crates/blockchain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ thiserror.workspace = true
sha3.workspace = true
tracing.workspace = true
bytes.workspace = true
cfg-if = "1.0.0"

ethereum_rust-rlp.workspace = true
ethereum_rust-core = { path = "../common", default-features = false }
Expand All @@ -29,3 +30,4 @@ libmdbx = [
"ethereum_rust-storage/default",
"ethereum_rust-vm/libmdbx",
]
levm = ["ethereum_rust-vm/levm"]
51 changes: 48 additions & 3 deletions crates/blockchain/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ use ethereum_rust_core::H256;

use ethereum_rust_storage::error::StoreError;
use ethereum_rust_storage::Store;
use ethereum_rust_vm::{
evm_state, execute_block, get_state_transitions, spec_id, EvmState, SpecId,
};
use ethereum_rust_vm::{evm_state, execute_block, spec_id, EvmState, SpecId};

//TODO: Implement a struct Chain or BlockChain to encapsulate
//functionality and canonical chain state and config
Expand All @@ -27,7 +25,10 @@ use ethereum_rust_vm::{
/// canonical chain/head. Fork choice needs to be updated for that in a separate step.
///
/// Performs pre and post execution validation, and updates the database with the post state.
#[cfg(not(feature = "levm"))]
pub fn add_block(block: &Block, storage: &Store) -> Result<(), ChainError> {
use ethereum_rust_vm::get_state_transitions;

let block_hash = block.header.compute_block_hash();

// Validate if it can be the new head and find the parent
Expand Down Expand Up @@ -63,6 +64,48 @@ pub fn add_block(block: &Block, storage: &Store) -> Result<(), ChainError> {
Ok(())
}

/// Adds a new block to the store. It may or may not be canonical, as long as its ancestry links
/// with the canonical chain and its parent's post-state is calculated. It doesn't modify the
/// canonical chain/head. Fork choice needs to be updated for that in a separate step.
///
/// Performs pre and post execution validation, and updates the database with the post state.
#[cfg(feature = "levm")]
pub fn add_block(block: &Block, storage: &Store) -> Result<(), ChainError> {
let block_hash = block.header.compute_block_hash();

// Validate if it can be the new head and find the parent
let Ok(parent_header) = find_parent_header(&block.header, storage) else {
// If the parent is not present, we store it as pending.
storage.add_pending_block(block.clone())?;
return Err(ChainError::ParentNotFound);
};
let mut state = evm_state(storage.clone(), block.header.parent_hash);

// Validate the block pre-execution
validate_block(block, &parent_header, &state)?;

let (receipts, account_updates) = execute_block(block, &mut state)?;

dbg!(&account_updates);

validate_gas_used(&receipts, &block.header)?;

// Apply the account updates over the last block's state and compute the new state root
let new_state_root = state
.database()
.ok_or(ChainError::StoreError(StoreError::MissingStore))?
.apply_account_updates(block.header.parent_hash, &account_updates)?
.ok_or(ChainError::ParentStateNotFound)?;

// Check state root matches the one in block header after execution
validate_state_root(&block.header, new_state_root)?;

store_block(storage, block.clone())?;
store_receipts(storage, receipts, block_hash)?;

Ok(())
}

/// Stores block and header in the database
pub fn store_block(storage: &Store, block: Block) -> Result<(), ChainError> {
storage.add_block(block)?;
Expand Down Expand Up @@ -166,6 +209,8 @@ pub fn validate_gas_used(
block_header: &BlockHeader,
) -> Result<(), ChainError> {
if let Some(last) = receipts.last() {
dbg!(last.cumulative_gas_used);
dbg!(block_header.gas_used);
if last.cumulative_gas_used != block_header.gas_used {
return Err(ChainError::InvalidBlock(InvalidBlockError::GasUsedMismatch));
}
Expand Down
1 change: 1 addition & 0 deletions crates/blockchain/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ fn apply_plain_transaction(

fn finalize_payload(context: &mut PayloadBuildContext) -> Result<(), StoreError> {
let account_updates = get_state_transitions(context.evm_state);
dbg!(&account_updates);
context.payload.header.state_root = context
.store()
.ok_or(StoreError::MissingStore)?
Expand Down
2 changes: 1 addition & 1 deletion crates/common/types/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1594,7 +1594,7 @@ mod serde_impl {
pub gas: Option<u64>,
#[serde(default)]
pub value: U256,
#[serde(default, with = "crate::serde_utils::bytes", alias = "data")]
#[serde(default, with = "crate::serde_utils::bytes")]
pub input: Bytes,
#[serde(default, with = "crate::serde_utils::u64::hex_str")]
pub gas_price: u64,
Expand Down
2 changes: 2 additions & 0 deletions crates/vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition = "2021"
[dependencies]
ethereum_rust-core = { path = "../common", default-features = false }
ethereum_rust-storage = { path = "../storage/store", default-features = false }
ethereum_rust_levm = { path = "./levm", optional = true }
revm = { version = "14.0.3", features = [
"serde",
"std",
Expand Down Expand Up @@ -38,3 +39,4 @@ l2 = []
c-kzg = ["revm/c-kzg"]
blst = ["revm/blst"]
libmdbx = ["ethereum_rust-storage/default", "ethereum_rust-core/libmdbx"]
levm = ["ethereum_rust_levm"]
42 changes: 42 additions & 0 deletions crates/vm/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,48 @@ pub struct StoreWrapper {
pub block_hash: BlockHash,
}

cfg_if::cfg_if! {
if #[cfg(feature = "levm")] {
use ethereum_rust_core::{U256 as CoreU256};
use ethereum_rust_levm::db::Database as LevmDatabase;

impl LevmDatabase for StoreWrapper {
fn get_account_info(&self, address: CoreAddress) -> ethereum_rust_levm::account::AccountInfo {
let acc_info = self
.store
.get_account_info_by_hash(self.block_hash, address)
.unwrap()
.unwrap_or_default();

let acc_code = self
.store
.get_account_code(acc_info.code_hash)
.unwrap()
.unwrap();

ethereum_rust_levm::account::AccountInfo {
balance: acc_info.balance,
nonce: acc_info.nonce,
bytecode: acc_code,
}
}

fn get_storage_slot(&self, address: CoreAddress, key: CoreH256) -> CoreU256 {
self.store
.get_storage_at_hash(self.block_hash, address, key)
.unwrap()
.unwrap_or_default()
}

fn get_block_hash(&self, block_number: u64) -> Option<CoreH256> {
let a = self.store.get_block_header(block_number).unwrap();

a.map(|a| CoreH256::from(a.compute_block_hash().0))
}
}
}
}

impl revm::Database for StoreWrapper {
type Error = StoreError;

Expand Down
2 changes: 1 addition & 1 deletion crates/vm/levm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "ethereum_rust-levm"
name = "ethereum_rust_levm"
version.workspace = true
edition.workspace = true

Expand Down
2 changes: 1 addition & 1 deletion crates/vm/levm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ help: ## 📚 Show help for each of the Makefile recipes
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

test: ## 🧪 Runs all tests except Ethereum tests
cargo test -p ethereum_rust-levm
cargo test -p ethereum_rust_levm

lint: ## 🧹 Linter check
cargo clippy --all-targets --all-features -- -D warnings
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/levm/bench/revm_comparison/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ name = "revm_comparison"
path = "src/lib.rs"

[dependencies]
ethereum_rust-levm = { path = "../../" }
ethereum_rust_levm = { path = "../../" }
hex = "0.4.3"
revm = "9.0.0"
bytes = "1.8.0"
Expand Down
6 changes: 1 addition & 5 deletions crates/vm/levm/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,7 @@ impl Cache {
}

pub fn get_storage_slot(&self, address: Address, key: H256) -> Option<StorageSlot> {
self.get_account(address)
.expect("Account should have been cached")
.storage
.get(&key)
.cloned()
self.get_account(address)?.storage.get(&key).cloned()
}

pub fn add_account(&mut self, address: &Address, account: &Account) {
Expand Down
12 changes: 12 additions & 0 deletions crates/vm/levm/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,15 @@ pub struct TransactionReport {
// a transaction could create multiple new contracts, but whatever.
pub created_address: Option<Address>,
}

impl TransactionReport {
/// Function to add gas to report without exceeding the maximum gas limit
pub fn add_gas_with_max(&mut self, gas: u64, max: u64) {
self.gas_used = self.gas_used.saturating_add(gas);
self.gas_used = self.gas_used.min(max);
JereSalo marked this conversation as resolved.
Show resolved Hide resolved
}

pub fn is_success(&self) -> bool {
matches!(self.result, TxResult::Success)
}
}
22 changes: 19 additions & 3 deletions crates/vm/levm/src/opcode_handlers/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,24 @@ impl VM {
.pop()?
.try_into()
.unwrap_or(usize::MAX);
let value = U256::from_big_endian(&current_call_frame.calldata.slice(offset..offset + 32));
current_call_frame.stack.push(value)?;

// This check is because if offset is larger than the calldata then we should push 0 to the stack.
let result = if offset < current_call_frame.calldata.len() {
// Read calldata from offset to the end
let calldata = current_call_frame.calldata.slice(offset..);

// Get the 32 bytes from the data slice, padding with 0 if fewer than 32 bytes are available
let mut padded_calldata = [0u8; 32];
let data_len_to_copy = calldata.len().min(32);

padded_calldata[..data_len_to_copy].copy_from_slice(&calldata[..data_len_to_copy]);

U256::from_big_endian(&padded_calldata)
} else {
U256::zero()
};

current_call_frame.stack.push(result)?;

Ok(OpcodeSuccess::Continue)
}
Expand Down Expand Up @@ -181,7 +197,7 @@ impl VM {
.stack
.push(U256::from(current_call_frame.bytecode.len()))?;

self.env.consumed_gas += gas_cost::CODESIZE;
self.increase_consumed_gas(current_call_frame, gas_cost::CODESIZE)?;

Ok(OpcodeSuccess::Continue)
}
Expand Down
3 changes: 1 addition & 2 deletions crates/vm/levm/src/opcode_handlers/push.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use ethereum_rust_core::U256;

use crate::{
call_frame::CallFrame,
constants::gas_cost,
errors::{OpcodeSuccess, VMError},
opcodes::Opcode,
vm::VM,
};
use ethereum_rust_core::U256;

// Push Operations
// Opcodes: PUSH0, PUSH1 ... PUSH32
Expand Down
33 changes: 24 additions & 9 deletions crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ impl VM {
}

// SSTORE operation
// TODO: add gas consumption
// TODO: add gas REFUNDS
pub fn op_sstore(
&mut self,
current_call_frame: &mut CallFrame,
Expand All @@ -177,21 +177,36 @@ impl VM {

let address = current_call_frame.to;

let original_value = if self.cache.is_slot_cached(&address, key) {
self.cache
.get_storage_slot(address, key)
.expect("Storage slot should have been cached")
.original_value
let mut base_dynamic_gas: U256 = U256::zero();

let storage_slot = if self.cache.is_slot_cached(&address, key) {
self.cache.get_storage_slot(address, key).unwrap()
} else {
// If slot is cold 2100 is added to base_dynamic_gas
base_dynamic_gas += U256::from(2100);

self.get_storage_slot(&address, key) // it is not in cache because of previous if
};

base_dynamic_gas += if value == storage_slot.current_value {
U256::from(100)
} else if storage_slot.current_value == storage_slot.original_value {
if storage_slot.original_value == U256::zero() {
U256::from(20000)
} else {
U256::from(2900)
}
} else {
self.cache_from_db(&address);
self.db.get_storage_slot(address, key)
U256::from(100)
};

self.increase_consumed_gas(current_call_frame, base_dynamic_gas)?;

self.cache.write_account_storage(
&address,
key,
StorageSlot {
original_value,
original_value: storage_slot.original_value,
current_value: value,
},
);
Expand Down
Loading
Loading