Skip to main content

axiom_eth/storage/
circuit.rs

1use ethers_core::types::{Address, Block, Chain, H256, U256};
2#[cfg(feature = "providers")]
3use ethers_providers::{JsonRpcClient, Provider};
4use halo2_base::{gates::GateInstructions, Context};
5use itertools::Itertools;
6use serde::{Deserialize, Serialize};
7use std::marker::PhantomData;
8use zkevm_hashes::util::eth_types::ToBigEndian;
9
10use crate::{
11    block_header::get_block_header_rlp_max_lens,
12    mpt::{MPTChip, MPTInput},
13    rlc::{circuit::builder::RlcCircuitBuilder, FIRST_PHASE},
14    utils::{
15        assign_vec, encode_addr_to_field, encode_h256_to_hilo, eth_circuit::EthCircuitInstructions,
16    },
17    Field,
18};
19
20use super::{
21    EIP1186ResponseDigest, EthBlockAccountStorageWitness, EthBlockStorageInputAssigned,
22    EthStorageChip, EthStorageInputAssigned,
23};
24
25#[derive(Clone, Debug, Hash, Serialize, Deserialize)]
26pub struct EthStorageInput {
27    pub addr: Address,
28    pub acct_pf: MPTInput,
29    pub acct_state: Vec<Vec<u8>>,
30    /// A vector of (slot, value, proof) tuples
31    pub storage_pfs: Vec<(U256, U256, MPTInput)>,
32}
33
34#[derive(Clone, Debug, Deserialize, Serialize)]
35pub struct EthBlockStorageInput {
36    pub block: Block<H256>,
37    pub block_number: u32,
38    pub block_hash: H256, // provided for convenience, actual block_hash is computed from block_header
39    pub block_header: Vec<u8>,
40    pub storage: EthStorageInput,
41}
42
43impl EthStorageInput {
44    pub fn assign<F: Field>(self, ctx: &mut Context<F>) -> EthStorageInputAssigned<F> {
45        let address = encode_addr_to_field(&self.addr);
46        let address = ctx.load_witness(address);
47        let acct_pf = self.acct_pf.assign(ctx);
48        let storage_pfs = self
49            .storage_pfs
50            .into_iter()
51            .map(|(slot, _, pf)| {
52                let slot = encode_h256_to_hilo(&H256(slot.to_be_bytes())).hi_lo();
53                let slot = slot.map(|slot| ctx.load_witness(slot));
54                let pf = pf.assign(ctx);
55                (slot, pf)
56            })
57            .collect();
58        EthStorageInputAssigned { address, acct_pf, storage_pfs }
59    }
60}
61
62impl EthBlockStorageInput {
63    pub fn assign<F: Field>(
64        self,
65        ctx: &mut Context<F>,
66        network: Chain,
67    ) -> EthBlockStorageInputAssigned<F> {
68        // let block_hash = encode_h256_to_field(&self.block_hash);
69        // let block_hash = block_hash.map(|block_hash| ctx.load_witness(block_hash));
70        let storage = self.storage.assign(ctx);
71        let max_len = get_block_header_rlp_max_lens(network).0;
72        let block_header = assign_vec(ctx, self.block_header, max_len);
73        EthBlockStorageInputAssigned { block_header, storage }
74    }
75}
76
77#[derive(Clone, Debug)]
78pub struct EthBlockStorageCircuit<F> {
79    pub inputs: EthBlockStorageInput, // public and private inputs
80    pub network: Chain,
81    _marker: PhantomData<F>,
82}
83
84impl<F> EthBlockStorageCircuit<F> {
85    pub fn new(inputs: EthBlockStorageInput, network: Chain) -> Self {
86        Self { inputs, network, _marker: PhantomData }
87    }
88
89    #[cfg(feature = "providers")]
90    pub fn from_provider<P: JsonRpcClient>(
91        provider: &Provider<P>,
92        block_number: u32,
93        address: Address,
94        slots: Vec<H256>,
95        acct_pf_max_depth: usize,
96        storage_pf_max_depth: usize,
97        network: Chain,
98    ) -> Self {
99        use crate::providers::storage::get_block_storage_input;
100
101        let inputs = get_block_storage_input(
102            provider,
103            block_number,
104            address,
105            slots,
106            acct_pf_max_depth,
107            storage_pf_max_depth,
108        );
109        Self::new(inputs, network)
110    }
111}
112
113impl<F: Field> EthCircuitInstructions<F> for EthBlockStorageCircuit<F> {
114    type FirstPhasePayload = (EthBlockAccountStorageWitness<F>, Chain);
115
116    fn virtual_assign_phase0(
117        &self,
118        builder: &mut RlcCircuitBuilder<F>,
119        mpt: &MPTChip<F>,
120    ) -> Self::FirstPhasePayload {
121        let chip = EthStorageChip::new(mpt, Some(self.network));
122        // ================= FIRST PHASE ================
123        let ctx = builder.base.main(FIRST_PHASE);
124        let input = self.inputs.clone().assign(ctx, self.network);
125        let (witness, digest) = chip.parse_eip1186_proofs_from_block_phase0(builder, input);
126        let EIP1186ResponseDigest {
127            block_hash,
128            block_number,
129            address,
130            slots_values,
131            address_is_empty,
132            slot_is_empty,
133        } = digest;
134        let assigned_instances = block_hash
135            .into_iter()
136            .chain([block_number, address])
137            .chain(slots_values.into_iter().flat_map(|(slot, value)| slot.into_iter().chain(value)))
138            .collect_vec();
139        assert_eq!(builder.base.assigned_instances.len(), 1);
140        builder.base.assigned_instances[0] = assigned_instances;
141        // For now this circuit is going to constrain that all slots are occupied. We can also create a circuit that exposes the bitmap of slot_is_empty
142        {
143            let ctx = builder.base.main(FIRST_PHASE);
144            mpt.gate().assert_is_const(ctx, &address_is_empty, &F::ZERO);
145            for slot_is_empty in slot_is_empty {
146                mpt.gate().assert_is_const(ctx, &slot_is_empty, &F::ZERO);
147            }
148        }
149        (witness, self.network)
150    }
151
152    fn virtual_assign_phase1(
153        &self,
154        builder: &mut RlcCircuitBuilder<F>,
155        mpt: &MPTChip<F>,
156        (witness, network): Self::FirstPhasePayload,
157    ) {
158        let chip = EthStorageChip::new(mpt, Some(network));
159        let _trace = chip.parse_eip1186_proofs_from_block_phase1(builder, witness);
160    }
161}