Skip to content

Commit 9a371e6

Browse files
committed
Ship JUMPDEST bitmaps in the witness (codes_jumpdests) — guest builds Code via from_parts_unchecked, no in-circuit bytecode scan (−1.88M cycles)
1 parent fc9adde commit 9a371e6

4 files changed

Lines changed: 52 additions & 5 deletions

File tree

crates/blockchain/blockchain.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2212,13 +2212,18 @@ impl Blockchain {
22122212
)
22132213
.map_err(|e| ChainError::WitnessGeneration(format!("witness record emission: {e}")))?;
22142214

2215+
let codes_jumpdests = codes
2216+
.iter()
2217+
.map(|c| ethrex_common::types::Code::compute_jumpdests(c).to_vec())
2218+
.collect();
22152219
Ok((
22162220
ExecutionWitness {
22172221
codes,
22182222
block_headers_bytes,
22192223
first_block_number: first_block_header.number,
22202224
chain_config: self.storage.get_chain_config(),
22212225
state_nodes,
2226+
codes_jumpdests,
22222227
},
22232228
block_access_lists,
22242229
))
@@ -2415,12 +2420,17 @@ impl Blockchain {
24152420
)
24162421
.map_err(|e| ChainError::WitnessGeneration(format!("witness record emission: {e}")))?;
24172422

2423+
let codes_jumpdests = codes
2424+
.iter()
2425+
.map(|c| ethrex_common::types::Code::compute_jumpdests(c).to_vec())
2426+
.collect();
24182427
Ok(ExecutionWitness {
24192428
codes,
24202429
block_headers_bytes,
24212430
first_block_number: parent_header.number,
24222431
chain_config: self.storage.get_chain_config(),
24232432
state_nodes,
2433+
codes_jumpdests,
24242434
})
24252435
}
24262436

crates/common/types/block_execution_witness.rs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,16 @@ pub struct ExecutionWitness {
8181
/// storage trie. The guest rebuilds the tries straight from the stream.
8282
#[rkyv(with = crate::rkyv_utils::VecVecWrapper)]
8383
pub state_nodes: Vec<Vec<u8>>,
84+
/// JUMPDEST bitmap for each entry of `codes` (parallel array, same order):
85+
/// one bit per bytecode byte, set when that offset holds a `JUMPDEST` that
86+
/// is not part of a `PUSH` immediate (exactly `Code::jumpdests`). Carrying
87+
/// it in the witness lets the guest build each `Code` with
88+
/// `Code::from_parts_unchecked` instead of re-scanning the whole bytecode
89+
/// corpus in-circuit. Trust model: like the shipped node hashes, a forged
90+
/// bitmap makes execution diverge (a JUMP wrongly succeeds or fails), so
91+
/// the final state root / receipts anchors reject the block.
92+
#[rkyv(with = crate::rkyv_utils::VecVecWrapper)]
93+
pub codes_jumpdests: Vec<Vec<u8>>,
8494
}
8595

8696
/// RPC-friendly representation of an execution witness.
@@ -218,12 +228,18 @@ impl RpcExecutionWitness {
218228
let initial_state_root = find_parent_state_root(decoded_headers, first_block_number)?;
219229
let nodes = node_map_from_rlp(self.state.iter());
220230
let state_nodes = witness_records_from_node_map(&nodes, initial_state_root, crypto)?;
231+
let codes_jumpdests = self
232+
.codes
233+
.iter()
234+
.map(|b| Code::compute_jumpdests(b).to_vec())
235+
.collect();
221236
Ok(ExecutionWitness {
222237
codes: self.codes.into_iter().map(|b| b.to_vec()).collect(),
223238
chain_config,
224239
first_block_number,
225240
block_headers_bytes: self.headers.into_iter().map(|b| b.to_vec()).collect(),
226241
state_nodes,
242+
codes_jumpdests,
227243
})
228244
}
229245
}
@@ -337,13 +353,19 @@ impl ExecutionWitness {
337353
let initial_state_root = find_parent_state_root(&headers, first_block_number)?;
338354
let nodes = node_map_from_rlp(input.witness.state_as_vecs().iter());
339355
let state_nodes = witness_records_from_node_map(&nodes, initial_state_root, crypto)?;
356+
let codes = input.witness.codes_as_vecs();
357+
let codes_jumpdests = codes
358+
.iter()
359+
.map(|c| Code::compute_jumpdests(c).to_vec())
360+
.collect();
340361

341362
Ok(Self {
342-
codes: input.witness.codes_as_vecs(),
363+
codes,
343364
block_headers_bytes,
344365
first_block_number,
345366
chain_config: amsterdam_chain_config(input.chain_id),
346367
state_nodes,
368+
codes_jumpdests,
347369
})
348370
}
349371
}
@@ -509,13 +531,25 @@ impl GuestProgramState {
509531
let (state_trie, storage_tries) =
510532
build_tries_from_records(&value.state_nodes, parent_header.state_root)?;
511533

512-
// hash codes
513-
// TODO: codes here probably needs to be Vec<Code>, rather than recomputing here. This requires rkyv implementation.
534+
// hash codes — the witness carries each code's JUMPDEST bitmap, so
535+
// building `Code` needs no in-circuit scan of the bytecode corpus.
536+
if value.codes.len() != value.codes_jumpdests.len() {
537+
return Err(GuestProgramStateError::Custom(format!(
538+
"witness carries {} codes but {} jumpdest bitmaps",
539+
value.codes.len(),
540+
value.codes_jumpdests.len()
541+
)));
542+
}
514543
let codes_hashed = value
515544
.codes
516545
.into_iter()
517-
.map(|code| {
518-
let code = Code::from_bytecode(code.into(), crypto);
546+
.zip(value.codes_jumpdests)
547+
.map(|(code, jumpdests)| {
548+
let code = Code::from_parts_unchecked(
549+
H256(crypto.keccak256(&code)),
550+
&code,
551+
std::sync::Arc::from(jumpdests.as_slice()),
552+
);
519553
(code.hash, code)
520554
})
521555
.collect();

crates/l2/sequencer/native_rollup/l1_advancer.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,7 @@ mod tests {
614614
..Default::default()
615615
},
616616
state_nodes: vec![],
617+
codes_jumpdests: vec![],
617618
};
618619

619620
// Block → SSZ (no BAL for pre-Amsterdam test block)
@@ -795,6 +796,7 @@ mod tests {
795796
..Default::default()
796797
},
797798
state_nodes: vec![],
799+
codes_jumpdests: vec![],
798800
};
799801

800802
// Block → SSZ with the real BAL passed through.

crates/networking/rpc/engine/payload.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1707,6 +1707,7 @@ mod tests {
17071707
first_block_number: 1,
17081708
chain_config: ChainConfig::default(),
17091709
state_nodes: vec![],
1710+
codes_jumpdests: vec![],
17101711
};
17111712

17121713
let encoded = encode_witness_for_engine_rpc(witness).unwrap();

0 commit comments

Comments
 (0)