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

feat(levm): run hive tests with levm, generate and publish report #1546

Merged
merged 46 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
0ed1d46
make some changes for testing purposes
JereSalo Dec 17, 2024
c8afde4
fix beacon contract levm (we use revm), quick fix gas used, fix lints…
JereSalo Dec 18, 2024
aae5d87
move get_account logic out of VM implementation
JereSalo Dec 18, 2024
fdf1bec
remove unnecessary caching in VM::new
JereSalo Dec 18, 2024
1f8b27f
replace one instance of get_account from db
JereSalo Dec 18, 2024
3649048
move creation of contract to transact, interact with cache first, the…
JereSalo Dec 18, 2024
6f3fa33
remove receiver account on revert (independently of transaction type)
JereSalo Dec 18, 2024
5bddf60
Delete .tool-versions~
JereSalo Dec 18, 2024
fd06635
remove file
JereSalo Dec 18, 2024
cfeaad4
Merge branch 'levm/fixes_cache_db' of github.com:lambdaclass/lambda_e…
JereSalo Dec 18, 2024
da8a681
remove cache_from_db method
JereSalo Dec 18, 2024
58d42b0
change get_account for access_account
JereSalo Dec 18, 2024
32435f2
replace direct access to db for cache and then db
JereSalo Dec 18, 2024
a49e264
Revert "change get_account for access_account"
JereSalo Dec 19, 2024
4979241
Merge branch 'main' into levm/fixes_cache_db
JereSalo Dec 20, 2024
d20451b
merge main
JereSalo Dec 20, 2024
fbeddef
add temporary cache for executing multiple transactions
JereSalo Dec 20, 2024
c58b936
extend account updates at the end of block and change get_state_trans…
JereSalo Dec 20, 2024
acf826b
Merge branch 'levm/fixes_cache_db' into levm/integration_l1_hive
JereSalo Dec 20, 2024
421d1ed
remove shallow-since and make non-functional changes
JereSalo Dec 20, 2024
ec30e64
fix logs
JereSalo Dec 20, 2024
cd3ac5b
tidy code and improve comments
JereSalo Dec 20, 2024
170a897
Merge branch 'levm/fix_logs' into levm/integration_l1_hive
JereSalo Dec 20, 2024
0293ed4
fix lint
JereSalo Dec 20, 2024
40135de
clean comments
JereSalo Dec 20, 2024
8d8783a
start refactor of get state transitions levm
JereSalo Dec 20, 2024
ffa1f48
Merge branch 'main' into levm/integration_l1_hive
JereSalo Dec 23, 2024
5ab4cef
begin with refactor to account updates
JereSalo Dec 23, 2024
56f3dcf
refactor get_state_transitions for levm
JereSalo Dec 23, 2024
d1b594a
fix my mistakes in get_state_transitions
JereSalo Dec 23, 2024
fa940d6
leave some things as they were
JereSalo Dec 23, 2024
bd468a3
restore files comparing with main
JereSalo Dec 23, 2024
e5381e4
clean code
JereSalo Dec 23, 2024
0393132
Merge branch 'main' into levm/integration_l1_hive
JereSalo Dec 23, 2024
958eb12
feat(levm): hive LEVM CI (#1567)
JereSalo Dec 27, 2024
2c6fcbf
Merge branch 'main' into levm/integration_l1_hive
JereSalo Dec 27, 2024
1af1f93
change something in get_state_transitions
JereSalo Dec 27, 2024
46d2c56
remove unnecessary or in account updates check
JereSalo Dec 27, 2024
41f15b4
change reports schedule, channel and file name
JereSalo Dec 27, 2024
82d33db
Merge branch 'main' into levm/integration_l1_hive
JereSalo Dec 27, 2024
30eecfe
change temporary_cache for block_cache
JereSalo Dec 27, 2024
4ad72e0
fix error and add make for running hive tests in levm
JereSalo Dec 27, 2024
cd42fd4
ditto
JereSalo Dec 27, 2024
8fa56fd
replace default-levm for levm
JereSalo Dec 27, 2024
81392e3
add if feature levm in ethrex.rs
JereSalo Dec 27, 2024
288ab52
update comment
JereSalo Dec 27, 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
22 changes: 22 additions & 0 deletions .github/scripts/publish_levm_hive.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
curl -X POST $url \
-H 'Content-Type: application/json; charset=utf-8' \
--data @- <<EOF
$(jq -n --arg text "$(cat results.md)" '{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "LEVM Hive Coverage Report"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": $text
}
}
]
}')
EOF
92 changes: 92 additions & 0 deletions .github/workflows/daily_reports_levm_hive.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: Daily Reports Hive LEVM

on:
schedule:
# Every day at UTC midnight
- cron: "0 0 * * 1,2,3,4,5"
workflow_dispatch:

env:
RUST_VERSION: 1.81.0

jobs:
run-hive:
name: Hive
runs-on: ubuntu-latest

steps:
- name: Checkout sources
uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5

- name: Build Image with LEVM
run: cd crates/vm/levm && make build-image-levm

- name: Setup Hive
run: make setup-hive

- name: Run Hive RPC Simulation
run: cd hive && ./hive --sim ethereum/rpc-compat --client ethrex --sim.limit "*" --sim.parallelism 16
continue-on-error: true

- name: Run Hive Sync Simulation
run: cd hive && ./hive --sim ethereum/sync --client ethrex --sim.limit "*" --sim.parallelism 16
continue-on-error: true

- name: Run Hive Engine Simulation
run: cd hive && ./hive --sim ethereum/engine --client ethrex --sim.limit "*" --sim.parallelism 16
continue-on-error: true

- name: Run Hive P2P Simulation
run: cd hive && ./hive --sim devp2p --client ethrex --sim.limit "*" --sim.parallelism 16
continue-on-error: true

- name: Upload results
uses: actions/upload-artifact@v4
with:
name: testing_logs
path: hive/workspace/logs/*-*.json

hive-report:
name: Generate report and upload to summary and slack
needs: run-hive
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4

- name: Rustup toolchain install
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}

- name: Download all results
uses: actions/download-artifact@v4
with:
path: hive/workspace/logs
pattern: "*_logs"
merge-multiple: true

- name: Caching
uses: Swatinem/rust-cache@v2

- name: Generate the hive report
run: cargo run -p hive_report > results.md

- name: Post results in summary
run: |
echo "# LEVM Hive coverage report" >> $GITHUB_STEP_SUMMARY
cat results.md >> $GITHUB_STEP_SUMMARY

- name: Post results to levm slack channel
env:
url: ${{ secrets.LEVM_SLACK_WEBHOOK }}
run: sh .github/scripts/publish_levm_hive.sh

# Note: Leave this commented, as it is for testing purposes.
# - name: Post results to test channel for debugging
# env:
# url: ${{ secrets.TEST_CHANNEL_SLACK }}
# run: sh .github/scripts/publish_levm_hive.sh
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ levm_ef_tests_summary_slack.txt
levm_ef_tests_summary_github.txt
levm_ef_tests_summary.txt

results.md
loc_report.md
loc_report_slack.txt
loc_report_github.txt
Expand Down
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ COPY --from=planner /ethrex/recipe.json recipe.json
# Build dependencies only, these remained cached
RUN cargo chef cook --release --recipe-path recipe.json

# Optional build flags
ARG BUILD_FLAGS=""
COPY . .
RUN cargo build --release
RUN cargo build --release $BUILD_FLAGS

FROM ubuntu:24.04
WORKDIR /usr/local/bin
Expand Down
2 changes: 1 addition & 1 deletion cmd/ethrex/ethrex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ fn import_blocks(store: &Store, blocks: &Vec<Block>) {
}
if let Some(last_block) = blocks.last() {
let hash = last_block.hash();
apply_fork_choice(store, hash, hash, hash).unwrap();
let _ = apply_fork_choice(store, hash, hash, hash);
}
info!("Added {} blocks to blockchain", size);
}
1 change: 1 addition & 0 deletions crates/blockchain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ path = "./blockchain.rs"

[features]
default = ["c-kzg"]
default-levm = ["default", "levm"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's name the target to levm

Suggested change
default-levm = ["default", "levm"]
levm = ["default", "levm"]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already a levm target
levm = ["ethrex-vm/levm"]
Do you think I should just do: levm = ["default", "ethrex-vm/levm"]?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libmdbx = [
"ethrex-core/libmdbx",
"ethrex-storage/default",
Expand Down
2 changes: 1 addition & 1 deletion crates/common/types/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub struct Account {
pub storage: HashMap<H256, U256>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Eq)]
pub struct AccountInfo {
pub code_hash: H256,
pub balance: U256,
Expand Down
11 changes: 11 additions & 0 deletions crates/l2/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ init-l1: ## 🚀 Initializes an L1 Lambda ethrex Client
--authrpc.port ${L1_AUTH_PORT} \
--datadir ${ethrex_L1_DEV_LIBMDBX}

init-l1-levm: ## 🚀 Initializes an L1 Lambda ethrex Client with LEVM
cargo run --release \
--manifest-path ../../Cargo.toml \
--bin ethrex \
--features dev,ethrex-blockchain/default-levm,ethrex-vm/default-levm -- \
--network ${L1_GENESIS_FILE_PATH} \
--http.port ${L1_PORT} \
--http.addr 0.0.0.0 \
--authrpc.port ${L1_AUTH_PORT} \
--datadir ${ethrex_L1_DEV_LIBMDBX}

down-local-l1: ## 🛑 Shuts down the L1 Lambda ethrex Client
docker compose -f ${ethrex_DEV_DOCKER_COMPOSE_PATH} down
docker compose -f docker-compose-l2.yaml down
Expand Down
2 changes: 1 addition & 1 deletion crates/storage/store/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub enum EngineType {
RedB,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AccountUpdate {
pub address: Address,
pub removed: bool,
Expand Down
1 change: 1 addition & 0 deletions crates/vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ path = "./vm.rs"

[features]
default = ["c-kzg", "blst"]
default-levm = ["default", "levm"]
JereSalo marked this conversation as resolved.
Show resolved Hide resolved
l2 = []
c-kzg = ["revm/c-kzg"]
blst = ["revm/blst"]
Expand Down
8 changes: 8 additions & 0 deletions crates/vm/levm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,11 @@ build-revm-comparison:
--bin levm_factorial \
--bin revm_fibonacci \
--bin levm_fibonacci

###### Build Client with LEVM ######

FLAGS := "--features ethrex-blockchain/default-levm,ethrex-vm/default-levm"

build-image-levm: ## 🐳 Build the Docker image with LEVM features
cd ../../../ && \
docker build -t ethrex --build-arg BUILD_FLAGS=$(FLAGS) .
122 changes: 86 additions & 36 deletions crates/vm/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,62 @@ cfg_if::cfg_if! {
use std::{collections::HashMap, sync::Arc};
use ethrex_core::types::code_hash;

pub fn get_state_transitions_levm(
initial_state: &EvmState,
block_hash: H256,
new_state: &CacheDB,
) -> Vec<AccountUpdate> {
let current_db = match initial_state {
EvmState::Store(state) => state.database.store.clone(),
EvmState::Execution(_cache_db) => unreachable!("Execution state should not be passed here"),
};
let mut account_updates: Vec<AccountUpdate> = vec![];
for (new_state_account_address, new_state_account) in new_state {
// This stores things that have changed in the account.
let mut account_update = AccountUpdate::new(*new_state_account_address);

// Account state before block execution.
let initial_account_state = current_db
.get_account_info_by_hash(block_hash, *new_state_account_address)
.expect("Error getting account info by address")
.unwrap_or_default();
// Account state after block execution.
let new_state_acc_info = AccountInfo {
code_hash: code_hash(&new_state_account.info.bytecode),
balance: new_state_account.info.balance,
nonce: new_state_account.info.nonce,
};

// Compare Account Info
if initial_account_state != new_state_acc_info {
account_update.info = Some(new_state_acc_info.clone());
}

// If code hash is different it means the code is different too.
if initial_account_state.code_hash != new_state_acc_info.code_hash {
account_update.code = Some(new_state_account.info.bytecode.clone());
}

let mut updated_storage = HashMap::new();
for (key, storage_slot) in &new_state_account.storage {
// original_value in storage_slot is not the original_value on the DB, be careful.
let original_value = current_db.get_storage_at_hash(block_hash, *new_state_account_address, *key).unwrap().unwrap_or_default(); // Option inside result, I guess I have to assume it is zero.

if original_value != storage_slot.current_value {
updated_storage.insert(*key, storage_slot.current_value);
}
}
account_update.added_storage = updated_storage;

account_update.removed = new_state_account.is_empty();

if account_update != AccountUpdate::new(*new_state_account_address) {
account_updates.push(account_update);
}
}
account_updates
}

/// Executes all transactions in a block and returns their receipts.
pub fn execute_block(
block: &Block,
Expand All @@ -103,56 +159,49 @@ cfg_if::cfg_if! {
}
}
}
let mut receipts = Vec::new();
let mut cumulative_gas_used = 0;

let store_wrapper = Arc::new(StoreWrapper {
store: state.database().unwrap().clone(),
block_hash: block.header.parent_hash,
});

let mut account_updates: Vec<AccountUpdate> = vec![];
// Account updates are initialized like this because of the beacon_root_contract_call, it is going to be empty if it wasn't called.
let mut account_updates = get_state_transitions(state);

for transaction in block.body.transactions.iter() {
let result = execute_tx_levm(transaction, block_header, store_wrapper.clone()).unwrap();
cumulative_gas_used += result.gas_used;
let receipt = Receipt::new(
transaction.tx_type(),
matches!(result.result, TxResult::Success),
cumulative_gas_used,
result.logs,
);
receipts.push(receipt);
let mut receipts = Vec::new();
let mut cumulative_gas_used = 0;
let mut temporary_cache: CacheDB = HashMap::new();
JereSalo marked this conversation as resolved.
Show resolved Hide resolved

for (address, account) in result.new_state {
let mut added_storage = HashMap::new();
for tx in block.body.transactions.iter() {
let report = execute_tx_levm(tx, block_header, store_wrapper.clone(), temporary_cache.clone()).unwrap();

for (key, value) in account.storage {
added_storage.insert(key, value.current_value);
let mut new_state = report.new_state.clone();

// Now original_value is going to be the same as the current_value, for the next transaction.
// It should have only one value but it is convenient to keep on using our CacheDB structure
for account in new_state.values_mut() {
for storage_slot in account.storage.values_mut() {
storage_slot.original_value = storage_slot.current_value;
}
}

let code = if account.info.bytecode.is_empty() {
None
} else {
Some(account.info.bytecode.clone())
};
temporary_cache.extend(new_state);

let account_update = AccountUpdate {
address,
removed: false,
info: Some(AccountInfo {
code_hash: code_hash(&account.info.bytecode),
balance: account.info.balance,
nonce: account.info.nonce,
}),
code,
added_storage,
};
// Currently, in LEVM, we don't substract refunded gas to used gas, but that can change in the future.
let gas_used = report.gas_used - report.gas_refunded;
cumulative_gas_used += gas_used;
let receipt = Receipt::new(
tx.tx_type(),
matches!(report.result.clone(), TxResult::Success),
cumulative_gas_used,
report.logs.clone(),
);

account_updates.push(account_update);
}
receipts.push(receipt);
}

account_updates.extend(get_state_transitions_levm(state, block.header.parent_hash, &temporary_cache));

if let Some(withdrawals) = &block.body.withdrawals {
process_withdrawals(state, withdrawals)?;
}
Expand All @@ -164,6 +213,7 @@ cfg_if::cfg_if! {
tx: &Transaction,
block_header: &BlockHeader,
db: Arc<dyn LevmDatabase>,
temporary_cache: CacheDB
JereSalo marked this conversation as resolved.
Show resolved Hide resolved
) -> Result<TransactionReport, VMError> {
let gas_price : U256 = tx.effective_gas_price(block_header.base_fee_per_gas).ok_or(VMError::InvalidTransaction)?.into();

Expand Down Expand Up @@ -194,7 +244,7 @@ cfg_if::cfg_if! {
tx.value(),
tx.data().clone(),
db,
CacheDB::default(),
temporary_cache,
tx.access_list(),
)?;

Expand Down
Loading