Skip to content

Commit e351ff9

Browse files
committed
Implement JumpDest fetching from RPC.
1 parent 5faa7a1 commit e351ff9

30 files changed

+730
-50
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ criterion = "0.5.1"
5757
dotenvy = "0.15.7"
5858
either = "1.12.0"
5959
enum-as-inner = "0.6.0"
60-
enumn = "0.1.13"
6160
env_logger = "0.11.3"
6261
eth_trie = "0.4.0"
6362
ethereum-types = "0.14.1"
@@ -94,7 +93,6 @@ serde = "1.0.203"
9493
serde-big-array = "0.5.1"
9594
serde_json = "1.0.118"
9695
serde_path_to_error = "0.1.16"
97-
serde_with = "3.8.1"
9896
sha2 = "0.10.8"
9997
static_assertions = "1.1.0"
10098
thiserror = "1.0.61"

evm_arithmetization/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ homepage.workspace = true
1515
keywords.workspace = true
1616

1717
[dependencies]
18+
__compat_primitive_types = { workspace = true }
1819
anyhow = { workspace = true }
1920
bytes = { workspace = true }
2021
env_logger = { workspace = true }

evm_arithmetization/benches/fibonacci_25m_gas.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ fn prepare_setup() -> anyhow::Result<GenerationInputs> {
195195
prev_hashes: vec![H256::default(); 256],
196196
cur_hash: H256::default(),
197197
},
198+
batch_jumpdest_table: None,
198199
})
199200
}
200201

evm_arithmetization/src/cpu/kernel/interpreter.rs

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ use crate::cpu::columns::CpuColumnsView;
1919
use crate::cpu::kernel::aggregator::KERNEL;
2020
use crate::cpu::kernel::constants::global_metadata::GlobalMetadata;
2121
use crate::generation::debug_inputs;
22+
use crate::generation::jumpdest::{ContextJumpDests, JumpDestTableProcessed, JumpDestTableWitness};
2223
use crate::generation::mpt::{load_linked_lists_and_txn_and_receipt_mpts, TrieRootPtrs};
24+
use crate::generation::prover_input::{get_proofs_and_jumpdests, CodeMap};
2325
use crate::generation::rlp::all_rlp_prover_inputs_reversed;
2426
use crate::generation::state::{
2527
all_ger_prover_inputs_reversed, all_withdrawals_prover_inputs_reversed, GenerationState,
@@ -56,6 +58,7 @@ pub(crate) struct Interpreter<F: Field> {
5658
/// Counts the number of appearances of each opcode. For debugging purposes.
5759
#[allow(unused)]
5860
pub(crate) opcode_count: [usize; 0x100],
61+
/// A table of contexts and their reached JUMPDESTs.
5962
jumpdest_table: HashMap<usize, BTreeSet<usize>>,
6063
/// `true` if the we are currently carrying out a jumpdest analysis.
6164
pub(crate) is_jumpdest_analysis: bool,
@@ -71,9 +74,10 @@ pub(crate) struct Interpreter<F: Field> {
7174
pub(crate) fn simulate_cpu_and_get_user_jumps<F: Field>(
7275
final_label: &str,
7376
state: &GenerationState<F>,
74-
) -> Option<HashMap<usize, Vec<usize>>> {
77+
// TODO(einar): remove second component of pair.
78+
) -> (Option<JumpDestTableProcessed>, ContextJumpDests) {
7579
match state.jumpdest_table {
76-
Some(_) => None,
80+
Some(_) => Default::default(),
7781
None => {
7882
let halt_pc = KERNEL.global_labels[final_label];
7983
let initial_context = state.registers.context;
@@ -94,14 +98,16 @@ pub(crate) fn simulate_cpu_and_get_user_jumps<F: Field>(
9498

9599
interpreter
96100
.generation_state
97-
.set_jumpdest_analysis_inputs(interpreter.jumpdest_table);
101+
.set_jumpdest_analysis_inputs(interpreter.jumpdest_table.clone());
98102

99103
log::debug!(
100104
"Simulated CPU for jumpdest analysis halted after {:?} cycles.",
101105
clock
102106
);
103-
104-
interpreter.generation_state.jumpdest_table
107+
(
108+
interpreter.generation_state.jumpdest_table,
109+
ContextJumpDests(interpreter.jumpdest_table),
110+
)
105111
}
106112
}
107113
}
@@ -114,7 +120,7 @@ pub(crate) struct ExtraSegmentData {
114120
pub(crate) withdrawal_prover_inputs: Vec<U256>,
115121
pub(crate) ger_prover_inputs: Vec<U256>,
116122
pub(crate) trie_root_ptrs: TrieRootPtrs,
117-
pub(crate) jumpdest_table: Option<HashMap<usize, Vec<usize>>>,
123+
pub(crate) jumpdest_table: Option<JumpDestTableProcessed>,
118124
pub(crate) next_txn_index: usize,
119125
}
120126

@@ -148,6 +154,49 @@ pub(crate) fn set_registers_and_run<F: Field>(
148154
interpreter.run()
149155
}
150156

157+
/// Computes the JUMPDEST proofs for each context.
158+
///
159+
/// # Arguments
160+
///
161+
/// - `jumpdest_table_rpc`: The raw table received from RPC.
162+
/// - `code_db`: The corresponding database of contract code used in the trace.
163+
pub(crate) fn set_jumpdest_analysis_inputs_rpc(
164+
jumpdest_table_rpc: &JumpDestTableWitness,
165+
code_map: &CodeMap,
166+
) -> JumpDestTableProcessed {
167+
let ctx_proofs = jumpdest_table_rpc
168+
.0
169+
.iter()
170+
.flat_map(|(code_addr, ctx_jumpdests)| {
171+
prove_context_jumpdests(&code_map.0[code_addr], ctx_jumpdests)
172+
})
173+
.collect();
174+
JumpDestTableProcessed(ctx_proofs)
175+
}
176+
177+
/// Orchestrates the proving of all contexts in a specific bytecode.
178+
///
179+
/// # Arguments
180+
///
181+
/// - `ctx_jumpdests`: Map from `ctx` to its list of offsets to reached
182+
/// `JUMPDEST`s.
183+
/// - `code`: The bytecode for the contexts. This is the same for all contexts.
184+
fn prove_context_jumpdests(
185+
code: &[u8],
186+
ctx_jumpdests: &ContextJumpDests,
187+
) -> HashMap<usize, Vec<usize>> {
188+
ctx_jumpdests
189+
.0
190+
.iter()
191+
.map(|(&ctx, jumpdests)| {
192+
let proofs = jumpdests.last().map_or(Vec::default(), |&largest_address| {
193+
get_proofs_and_jumpdests(code, largest_address, jumpdests.clone())
194+
});
195+
(ctx, proofs)
196+
})
197+
.collect()
198+
}
199+
151200
impl<F: Field> Interpreter<F> {
152201
/// Returns an instance of `Interpreter` given `GenerationInputs`, and
153202
/// assuming we are initializing with the `KERNEL` code.

evm_arithmetization/src/cpu/kernel/tests/add11.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ fn test_add11_yml() {
196196
prev_hashes: vec![H256::default(); 256],
197197
cur_hash: H256::default(),
198198
},
199+
batch_jumpdest_table: None,
199200
};
200201

201202
let initial_stack = vec![];
@@ -378,6 +379,7 @@ fn test_add11_yml_with_exception() {
378379
prev_hashes: vec![H256::default(); 256],
379380
cur_hash: H256::default(),
380381
},
382+
batch_jumpdest_table: None,
381383
};
382384

383385
let initial_stack = vec![];

evm_arithmetization/src/cpu/kernel/tests/core/jumpdest_analysis.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use plonky2::field::goldilocks_field::GoldilocksField as F;
77
use crate::cpu::kernel::aggregator::KERNEL;
88
use crate::cpu::kernel::interpreter::Interpreter;
99
use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode};
10+
use crate::generation::jumpdest::JumpDestTableProcessed;
1011
use crate::witness::operation::CONTEXT_SCALING_FACTOR;
1112

1213
#[test]
@@ -67,7 +68,10 @@ fn test_jumpdest_analysis() -> Result<()> {
6768
interpreter.generation_state.jumpdest_table,
6869
// Context 3 has jumpdest 1, 5, 7. All have proof 0 and hence
6970
// the list [proof_0, jumpdest_0, ... ] is [0, 1, 0, 5, 0, 7, 8, 40]
70-
Some(HashMap::from([(3, vec![0, 1, 0, 5, 0, 7, 8, 40])]))
71+
Some(JumpDestTableProcessed(HashMap::from([(
72+
3,
73+
vec![0, 1, 0, 5, 0, 7, 8, 40]
74+
)])))
7175
);
7276

7377
// Run jumpdest analysis with context = 3
@@ -89,6 +93,7 @@ fn test_jumpdest_analysis() -> Result<()> {
8993
.jumpdest_table
9094
.as_mut()
9195
.unwrap()
96+
.0
9297
.get_mut(&CONTEXT)
9398
.unwrap()
9499
.pop();
@@ -136,7 +141,8 @@ fn test_packed_verification() -> Result<()> {
136141
let mut interpreter: Interpreter<F> =
137142
Interpreter::new(write_table_if_jumpdest, initial_stack.clone(), None);
138143
interpreter.set_code(CONTEXT, code.clone());
139-
interpreter.generation_state.jumpdest_table = Some(HashMap::from([(3, vec![1, 33])]));
144+
interpreter.generation_state.jumpdest_table =
145+
Some(JumpDestTableProcessed(HashMap::from([(3, vec![1, 33])])));
140146

141147
interpreter.run()?;
142148

@@ -149,7 +155,8 @@ fn test_packed_verification() -> Result<()> {
149155
let mut interpreter: Interpreter<F> =
150156
Interpreter::new(write_table_if_jumpdest, initial_stack.clone(), None);
151157
interpreter.set_code(CONTEXT, code.clone());
152-
interpreter.generation_state.jumpdest_table = Some(HashMap::from([(3, vec![1, 33])]));
158+
interpreter.generation_state.jumpdest_table =
159+
Some(JumpDestTableProcessed(HashMap::from([(3, vec![1, 33])])));
153160

154161
assert!(interpreter.run().is_err());
155162

evm_arithmetization/src/cpu/kernel/tests/init_exc_stop.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ fn test_init_exc_stop() {
111111
cur_hash: H256::default(),
112112
},
113113
global_exit_roots: vec![],
114+
batch_jumpdest_table: None,
114115
};
115116
let initial_stack = vec![];
116117
let initial_offset = KERNEL.global_labels["init"];
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use std::{
2+
collections::{BTreeSet, HashMap},
3+
fmt::Display,
4+
};
5+
6+
use keccak_hash::H256;
7+
use serde::{Deserialize, Serialize};
8+
9+
/// Each `CodeAddress` can be called one or more times, each time creating a new
10+
/// `Context`. Each `Context` will contain one or more offsets of `JUMPDEST`.
11+
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, Default)]
12+
pub struct ContextJumpDests(pub HashMap<usize, BTreeSet<usize>>);
13+
14+
/// The result after proving a `JumpDestTableWitness`.
15+
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, Default)]
16+
pub(crate) struct JumpDestTableProcessed(pub HashMap<usize, Vec<usize>>);
17+
18+
/// Map `CodeAddress -> (Context -> [JumpDests])`
19+
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, Default)]
20+
pub struct JumpDestTableWitness(pub HashMap<H256, ContextJumpDests>);
21+
22+
impl JumpDestTableWitness {
23+
/// Insert `offset` into `ctx` under the corrresponding `code_hash`.
24+
/// Creates the required `ctx` keys and `code_hash`. Idempotent.
25+
pub fn insert(&mut self, code_hash: &H256, ctx: usize, offset: usize) {
26+
self.0.entry(*code_hash).or_default();
27+
28+
self.0.get_mut(code_hash).unwrap().0.entry(ctx).or_default();
29+
30+
self.0
31+
.get_mut(code_hash)
32+
.unwrap()
33+
.0
34+
.get_mut(&ctx)
35+
.unwrap()
36+
.insert(offset);
37+
38+
// TODO(einar) remove before publishing PR.
39+
assert!(self.0.contains_key(code_hash));
40+
assert!(self.0[code_hash].0.contains_key(&ctx));
41+
assert!(self.0[code_hash].0[&ctx].contains(&offset));
42+
}
43+
}
44+
45+
impl Display for JumpDestTableWitness {
46+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47+
writeln!(f, "=== JumpDest table ===")?;
48+
49+
for (code, ctxtbls) in &self.0 {
50+
write!(f, "codehash: {:?}\n{}", code, ctxtbls)?;
51+
}
52+
Ok(())
53+
}
54+
}
55+
56+
impl Display for ContextJumpDests {
57+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58+
for (ctx, offsets) in &self.0 {
59+
write!(f, " ctx: {}, offsets: [", ctx)?;
60+
for offset in offsets {
61+
write!(f, "{:#10x} ", offset)?;
62+
}
63+
writeln!(f, "]")?;
64+
}
65+
Ok(())
66+
}
67+
}

evm_arithmetization/src/generation/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::collections::HashMap;
33
use anyhow::anyhow;
44
use ethereum_types::H160;
55
use ethereum_types::{Address, BigEndianHash, H256, U256};
6+
use jumpdest::JumpDestTableWitness;
67
use keccak_hash::keccak;
78
use log::log_enabled;
89
use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie};
@@ -34,6 +35,7 @@ use crate::util::{h2u, u256_to_usize};
3435
use crate::witness::memory::{MemoryAddress, MemoryChannel, MemoryState};
3536
use crate::witness::state::RegistersState;
3637

38+
pub mod jumpdest;
3739
pub(crate) mod linked_list;
3840
pub mod mpt;
3941
pub(crate) mod prover_input;
@@ -99,6 +101,10 @@ pub struct GenerationInputs {
99101
/// The hash of the current block, and a list of the 256 previous block
100102
/// hashes.
101103
pub block_hashes: BlockHashes,
104+
105+
/// A table listing each JUMPDESTs reached in each call context under
106+
/// associated code hash.
107+
pub batch_jumpdest_table: Option<JumpDestTableWitness>,
102108
}
103109

104110
/// A lighter version of [`GenerationInputs`], which have been trimmed
@@ -145,6 +151,10 @@ pub struct TrimmedGenerationInputs {
145151
/// The hash of the current block, and a list of the 256 previous block
146152
/// hashes.
147153
pub block_hashes: BlockHashes,
154+
155+
/// A list of tables listing each JUMPDESTs reached in each call context
156+
/// under associated code hash.
157+
pub batch_jumpdest_table: Option<JumpDestTableWitness>,
148158
}
149159

150160
#[derive(Clone, Debug, Deserialize, Serialize, Default)]
@@ -218,6 +228,7 @@ impl GenerationInputs {
218228
burn_addr: self.burn_addr,
219229
block_metadata: self.block_metadata.clone(),
220230
block_hashes: self.block_hashes.clone(),
231+
batch_jumpdest_table: self.batch_jumpdest_table.clone(),
221232
}
222233
}
223234
}

0 commit comments

Comments
 (0)