Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5c404bd
Charge the EIP-8141 frame entry access and wire up the Bogota spec tests
ilitteri Aug 12, 2026
50a5893
Reconcile the block access list when a frame reverts on its own
ilitteri Aug 12, 2026
3c06d01
Satisfy clippy on the frame entry charge
ilitteri Aug 20, 2026
78196de
Charge the EIP-8037 NEW_ACCOUNT state cost when a frame revives a dea…
ilitteri Aug 12, 2026
2431c0a
Run a frame's target through the EVM unless it is a codeless VERIFY t…
ilitteri Aug 12, 2026
c4ee3c8
Ignore partial fixture-bundle downloads
Aug 12, 2026
ac0ed1c
Widen a frame transaction's fee fields to U256 and run the released s…
ilitteri Aug 18, 2026
b4f25ff
Narrow the fee accessors at the L2 fee-arithmetic test site
ilitteri Aug 18, 2026
c316691
Exclude two bot-blocked hosts from the docs link check
ilitteri Aug 20, 2026
60fd99e
Record the unimplemented two-dimensional gas revision of EIP-8141
ilitteri Aug 26, 2026
f93ccfd
Clamp the frame-transaction priority fee instead of panicking on over…
ilitteri Aug 26, 2026
ff7ddf0
Pin the spec target to the SIGPARAM operand revision the code implements
ilitteri Aug 26, 2026
c18b9a8
Describe the revised frame encoding as nesting in place, not moving
ilitteri Aug 26, 2026
7e95701
Implement the two-dimensional gas model of EIP-8141
ilitteri Aug 27, 2026
bdb34a9
Pin the frames spec tests to tests-frames-devnet@v0.1.0
ilitteri Aug 27, 2026
4b672ca
Pin the revised frame transaction and receipt layouts with byte vectors
ilitteri Aug 27, 2026
34fe233
Declare frame state budgets in the EIP-8141 unit tests
ilitteri Aug 27, 2026
2a5a18b
List SIGDATACOPY in the frames doc and fix its limitation link
ilitteri Aug 27, 2026
947c51a
Pin the frames doc to the spec revision the fixtures declare
ilitteri Aug 27, 2026
e7c820b
Make the ef-test frame conversion total, as its comment says it is
ilitteri Aug 27, 2026
18bb089
Describe the frame receipt's two-dimensional gas where the layout is …
ilitteri Aug 28, 2026
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ jwt.hex
/tmp

*tests*.tar.gz
# Partial downloads of the fixture bundles; the Makefile fetches to `.part`
# first so a failed download cannot truncate the previous bundle.
*.tar.gz.part
tooling/ef_tests/state/test.tar.gz

.env
Expand Down
31 changes: 30 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
.PHONY: build lint test clean run-image build-image clean-vectors \
setup-hive test-pattern-default run-hive run-hive-debug clean-hive-logs \
load-test-fibonacci load-test-io run-hive-eels-blobs run-hive-eels-amsterdam \
run-hive-eels-bal-quick run-hive-build-block bench-rlp zkevm-bench-setup
run-hive-eels-bal-quick run-hive-build-block bench-rlp zkevm-bench-setup \
patch-hive-frames-fork run-hive-eels-frames run-hive-eels-frames-rlp \
run-hive-eels-frames-quick

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}'
Expand Down Expand Up @@ -172,6 +174,33 @@ run-hive-eels-amsterdam: build-image setup-hive ## 🧪 Run hive EELS Amsterdam
run-hive-eels-bal-quick: build-image setup-hive ## 🧪 Run hive EELS quick tests for the Amsterdam EIPs
- cd hive && ./hive --client-file $(HIVE_CLIENT_FILE) --client ethrex --sim ethereum/eels/consume-engine --sim.limit ".*(2780|7708|7732|7778|7843|7928|7954|7975|7976|7981|7997|8024|8037|8038|8045|8061|8070|8159|8246|8282).*" --sim.parallelism $(SIM_PARALLELISM) --sim.loglevel $(SIM_LOG_LEVEL) --sim.buildarg fixtures=$(AMSTERDAM_FIXTURES_URL) --sim.buildarg branch=$(AMSTERDAM_FIXTURES_BRANCH)

FRAMES_FIXTURES_URL ?= $(shell cat tooling/ef_tests/.fixtures_url_frames)
FRAMES_FIXTURES_BRANCH ?= devnets/frames/0
# The frames release refills the WHOLE suite at Bogota, so the default sweep is
# every Bogota fixture; `run-hive-eels-frames-quick` narrows it to EIP-8141.
FRAMES_FORK_PATTERN ?= .*fork_Bogota.*
FRAMES_QUICK_PATTERN ?= .*8141.*

# Hive's ethrex client definition maps HIVE_<FORK>_TIMESTAMP env vars onto genesis
# fields, and it stops at Amsterdam -- so the Bogota timestamp EEST sets for these
# fixtures reaches the client as nothing at all, frame transactions stay pre-fork,
# and every EIP-8141 test fails while the rest of the Bogota suite passes. Patch
# the mapper in the clone until it carries the field upstream. `git checkout` first
# so repeated runs do not stack the same line, mirroring run-hive-build-block.
patch-hive-frames-fork:
cd hive && git checkout -- clients/ethrex/mapper.jq
cd hive && sed -i 's/\( *\)"bpo1Time": env.HIVE_BPO1_TIMESTAMP|to_int,/\1"bogotaTime": env.HIVE_BOGOTA_TIMESTAMP|to_int,\n\1"bpo1Time": env.HIVE_BPO1_TIMESTAMP|to_int,/' clients/ethrex/mapper.jq
@grep -q bogotaTime hive/clients/ethrex/mapper.jq || { echo "failed to patch hive mapper for the Bogota fork"; exit 1; }

run-hive-eels-frames: build-image setup-hive patch-hive-frames-fork ## 🧪 Run hive EELS frames-devnet Engine tests
- cd hive && ./hive --client-file $(HIVE_CLIENT_FILE) --client ethrex --sim ethereum/eels/consume-engine --sim.limit "$(FRAMES_FORK_PATTERN)" --sim.parallelism $(SIM_PARALLELISM) --sim.loglevel $(SIM_LOG_LEVEL) --sim.buildarg fixtures=$(FRAMES_FIXTURES_URL) --sim.buildarg branch=$(FRAMES_FIXTURES_BRANCH)

run-hive-eels-frames-rlp: build-image setup-hive patch-hive-frames-fork ## 🧪 Run hive EELS frames-devnet RLP tests
- cd hive && ./hive --client-file $(HIVE_CLIENT_FILE) --client ethrex --sim ethereum/eels/consume-rlp --sim.limit "$(FRAMES_FORK_PATTERN)" --sim.parallelism $(SIM_PARALLELISM) --sim.loglevel $(SIM_LOG_LEVEL) --sim.buildarg fixtures=$(FRAMES_FIXTURES_URL) --sim.buildarg branch=$(FRAMES_FIXTURES_BRANCH)

run-hive-eels-frames-quick: build-image setup-hive patch-hive-frames-fork ## 🧪 Run hive EELS frames-devnet tests for EIP-8141 only
- cd hive && ./hive --client-file $(HIVE_CLIENT_FILE) --client ethrex --sim ethereum/eels/consume-engine --sim.limit "$(FRAMES_QUICK_PATTERN)" --sim.parallelism $(SIM_PARALLELISM) --sim.loglevel $(SIM_LOG_LEVEL) --sim.buildarg fixtures=$(FRAMES_FIXTURES_URL) --sim.buildarg branch=$(FRAMES_FIXTURES_BRANCH)

# Block-building simulator (execution-specs PR #2679). Not yet upstream in Hive,
# so we install the simulator Dockerfile into the hive clone and patch the
# ethrex hive client to expose the `testing` namespace (testing_buildBlockV1
Expand Down
2 changes: 1 addition & 1 deletion crates/blockchain/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3598,7 +3598,7 @@ impl Blockchain {
}

// Check priority fee is less or equal than gas fee gap
if tx.max_priority_fee().unwrap_or(0) > tx.max_fee_per_gas().unwrap_or(0) {
if tx.max_priority_fee().unwrap_or_default() > tx.max_fee_per_gas().unwrap_or_default() {
return Err(MempoolError::TxTipAboveFeeCapError);
}

Expand Down
144 changes: 128 additions & 16 deletions crates/common/types/receipt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,49 @@ pub const FRAME_RECEIPT_STATUS_FAILURE: u8 = 0;
pub const FRAME_RECEIPT_STATUS_SUCCESS: u8 = 1;
pub const FRAME_RECEIPT_STATUS_SKIPPED: u8 = 2;

/// A frame's gas usage, one entry per gas dimension of EIP-8037 (EIP-8141).
/// Encoded as the nested two-element list `[execution, state]`.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
pub struct GasUsed {
/// Execution gas the frame's meter consumed.
pub execution: u64,
/// State gas attributed to the frame: its declared budget less what remained
/// in its pool at frame exit. Not final until the transaction ends — a later
/// frame's cross-frame refill lowers the owning frame's entry in place, and an
/// atomic-batch unroll zeroes the state gas of the frames it unrolls.
pub state: u64,
}

impl GasUsed {
/// The frame's total gas usage across both dimensions.
pub fn total(&self) -> u64 {
self.execution.saturating_add(self.state)
}
}

impl RLPEncode for GasUsed {
fn encode(&self, buf: &mut dyn bytes::BufMut) {
Encoder::new(buf)
.encode_field(&self.execution)
.encode_field(&self.state)
.finish();
}
}

impl RLPDecode for GasUsed {
fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
let decoder = Decoder::new(rlp)?;
let (execution, decoder) = decoder.decode_field("execution")?;
let (state, decoder) = decoder.decode_field("state")?;
Ok((GasUsed { execution, state }, decoder.finish()?))
}
}

/// Per-frame execution result within a frame transaction (EIP-8141)
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct FrameReceipt {
pub status: u8,
pub gas_used: u64,
pub gas_used: GasUsed,
pub logs: Vec<Log>,
}

Expand Down Expand Up @@ -771,7 +809,10 @@ mod test {
fn test_frame_receipt_rlp_roundtrip() {
let fr = FrameReceipt {
status: FRAME_RECEIPT_STATUS_SUCCESS,
gas_used: 21000,
gas_used: GasUsed {
execution: 21000,
state: 0,
},
logs: vec![Log {
address: Address::random(),
topics: vec![],
Expand All @@ -785,10 +826,14 @@ mod test {

#[test]
fn test_frame_receipt_skipped_status_rlp_roundtrip() {
// Spec line 137: status code 0x3 marks frames skipped by a failed atomic batch.
// EIP-8141 `FrameStatus.SKIPPED` is 2, and marks only frames that never
// ran because an earlier frame of their atomic batch failed.
let fr = FrameReceipt {
status: FRAME_RECEIPT_STATUS_SKIPPED,
gas_used: 0,
gas_used: GasUsed {
execution: 0,
state: 0,
},
logs: vec![],
};
let encoded = fr.encode_to_vec();
Expand All @@ -811,12 +856,18 @@ mod test {
frame_receipts: Some(vec![
FrameReceipt {
status: FRAME_RECEIPT_STATUS_SUCCESS,
gas_used: 100000,
gas_used: GasUsed {
execution: 100000,
state: 0,
},
logs: vec![],
},
FrameReceipt {
status: FRAME_RECEIPT_STATUS_SUCCESS,
gas_used: 200000,
gas_used: GasUsed {
execution: 200000,
state: 0,
},
logs: vec![Log {
address: Address::from_low_u64_be(0xbeef),
topics: vec![],
Expand Down Expand Up @@ -846,12 +897,18 @@ mod test {
frame_receipts: Some(vec![
FrameReceipt {
status: FRAME_RECEIPT_STATUS_SUCCESS,
gas_used: 50000,
gas_used: GasUsed {
execution: 50000,
state: 0,
},
logs: vec![],
},
FrameReceipt {
status: FRAME_RECEIPT_STATUS_FAILURE,
gas_used: 50000,
gas_used: GasUsed {
execution: 50000,
state: 0,
},
logs: vec![],
},
]),
Expand All @@ -874,12 +931,18 @@ mod test {
frame_receipts: Some(vec![
FrameReceipt {
status: FRAME_RECEIPT_STATUS_SUCCESS,
gas_used: 50000,
gas_used: GasUsed {
execution: 50000,
state: 0,
},
logs: vec![],
},
FrameReceipt {
status: FRAME_RECEIPT_STATUS_SKIPPED,
gas_used: 0,
gas_used: GasUsed {
execution: 0,
state: 0,
},
logs: vec![],
},
]),
Expand All @@ -905,12 +968,18 @@ mod test {
frame_receipts: Some(vec![
FrameReceipt {
status: FRAME_RECEIPT_STATUS_FAILURE,
gas_used: 1000,
gas_used: GasUsed {
execution: 1000,
state: 0,
},
logs: vec![],
}, // a DEFAULT frame failed
FrameReceipt {
status: FRAME_RECEIPT_STATUS_SUCCESS,
gas_used: 2000,
gas_used: GasUsed {
execution: 2000,
state: 0,
},
logs: vec![log],
},
]),
Expand All @@ -929,6 +998,37 @@ mod test {
assert_eq!(r.encode_storage(), r.encode_inner());
}

/// Golden byte vector for the EIP-8141 two-dimensional `gas_used`. Every other
/// test in this file builds its expectation with the same encoder, so none of
/// them would notice the nested `[execution, state]` list flattening back to a
/// scalar; only the spec-test fixtures would, through the receipts root. This
/// pins the layout without them.
#[test]
fn frame_receipt_gas_used_encodes_as_a_nested_pair() {
// [21000, 0] -> c4 82 52 08 80
assert_eq!(
GasUsed {
execution: 21_000,
state: 0,
}
.encode_to_vec(),
vec![0xc4, 0x82, 0x52, 0x08, 0x80]
);
// [status, [execution, state], logs] -> c7 01 c4 82 52 08 80 c0
assert_eq!(
FrameReceipt {
status: FRAME_RECEIPT_STATUS_SUCCESS,
gas_used: GasUsed {
execution: 21_000,
state: 0,
},
logs: Vec::new(),
}
.encode_to_vec(),
vec![0xc7, 0x01, 0xc4, 0x82, 0x52, 0x08, 0x80, 0xc0]
);
}

#[test]
fn frame_receipt_trie_encoding_is_eip8141_payload() {
let receipt = Receipt {
Expand All @@ -939,7 +1039,10 @@ mod test {
payer: Some(Address::from_low_u64_be(0xBEEF)),
frame_receipts: Some(vec![FrameReceipt {
status: FRAME_RECEIPT_STATUS_SUCCESS,
gas_used: 21_000,
gas_used: GasUsed {
execution: 21_000,
state: 0,
},
logs: Vec::new(),
}]),
};
Expand Down Expand Up @@ -1010,12 +1113,18 @@ mod test {
frame_receipts: Some(vec![
FrameReceipt {
status: FRAME_RECEIPT_STATUS_SUCCESS,
gas_used: 100000,
gas_used: GasUsed {
execution: 100000,
state: 0,
},
logs: vec![],
},
FrameReceipt {
status: FRAME_RECEIPT_STATUS_SUCCESS,
gas_used: 200000,
gas_used: GasUsed {
execution: 200000,
state: 0,
},
logs: vec![Log {
address: Address::from_low_u64_be(0xbeef),
topics: vec![],
Expand Down Expand Up @@ -1047,7 +1156,10 @@ mod test {
payer: None,
frame_receipts: Some(vec![FrameReceipt {
status: FRAME_RECEIPT_STATUS_SUCCESS,
gas_used: 21000,
gas_used: GasUsed {
execution: 21000,
state: 0,
},
logs: vec![],
}]),
};
Expand Down
Loading
Loading