use crate::Field;
use crate::{
keccak::KeccakChip,
mpt::{MPTChip, MPTProof},
rlc::{
chip::RlcChip,
circuit::builder::RlcContextPair,
concat_array::{concat_var_fixed_array_phase0, concat_var_fixed_array_phase1},
types::{AssignedVarLenVec, ConcatVarFixedArrayWitness},
},
rlp::RlpChip,
solidity::types::VarMappingTrace,
storage::{EthStorageChip, EthStorageTrace, EthStorageWitness},
};
use halo2_base::{
gates::{GateChip, GateInstructions, RangeChip, RangeInstructions},
safe_types::{SafeBytes32, SafeTypeChip, VarLenBytesVec},
AssignedValue, Context,
QuantumCell::Constant,
};
use itertools::Itertools;
use self::types::{
MappingTrace, MappingWitness, NestedMappingWitness, SolidityType, VarMappingWitness,
};
#[cfg(all(test, feature = "providers"))]
pub mod tests;
pub mod types;
pub struct SolidityChip<'chip, F: Field> {
pub mpt: &'chip MPTChip<'chip, F>,
pub max_nesting: usize,
pub max_key_byte_len: usize, }
impl<'chip, F: Field> SolidityChip<'chip, F> {
pub fn new(mpt: &'chip MPTChip<'chip, F>, max_nesting: usize, max_key_byte_len: usize) -> Self {
Self { mpt, max_nesting, max_key_byte_len }
}
pub fn gate(&self) -> &GateChip<F> {
self.mpt.gate()
}
pub fn range(&self) -> &RangeChip<F> {
self.mpt.range()
}
pub fn rlc(&self) -> &RlcChip<F> {
self.mpt.rlc()
}
pub fn rlp(&self) -> RlpChip<F> {
self.mpt.rlp()
}
pub fn keccak(&self) -> &KeccakChip<F> {
self.mpt.keccak()
}
pub fn slot_for_mapping_value_key(
&self,
ctx: &mut Context<F>,
mapping_slot: &SafeBytes32<F>,
key: &SafeBytes32<F>,
) -> SafeBytes32<F> {
let key_slot_concat = [key.value(), mapping_slot.value()].concat();
debug_assert_eq!(key_slot_concat.len(), 64);
let hash_query = self.keccak().keccak_fixed_len(ctx, key_slot_concat);
hash_query.output_bytes
}
pub fn slot_for_mapping_nonvalue_key_phase0(
&self,
ctx: &mut Context<F>,
mapping_slot: SafeBytes32<F>,
key: VarLenBytesVec<F>,
) -> VarMappingWitness<F> {
let key_values = key.bytes().iter().map(|b| *b.as_ref()).collect();
let prefix = AssignedVarLenVec { values: key_values, len: *key.len() };
let suffix = mapping_slot.value().to_vec();
let concat_witness = concat_var_fixed_array_phase0(ctx, self.gate(), prefix, suffix);
let concat_values = concat_witness.concat.values;
let concat_len = concat_witness.concat.len;
let hash_query = self.keccak().keccak_var_len(ctx, concat_values, concat_len, 32);
VarMappingWitness { mapping_slot, key, hash_query }
}
pub fn slot_for_mapping_nonvalue_key_phase1(
&self,
(ctx_gate, ctx_rlc): RlcContextPair<F>,
witness: VarMappingWitness<F>,
) -> VarMappingTrace<F> {
let VarMappingWitness { mapping_slot, key, hash_query } = witness;
let key_values = key.bytes().iter().map(|b| *b.as_ref()).collect();
let prefix = AssignedVarLenVec { values: key_values, len: *key.len() };
let suffix = mapping_slot.value().to_vec();
let concat =
AssignedVarLenVec { values: hash_query.input_assigned.clone(), len: hash_query.length };
let witness = ConcatVarFixedArrayWitness { prefix, suffix, concat };
concat_var_fixed_array_phase1((ctx_gate, ctx_rlc), self.gate(), self.rlc(), witness)
}
pub fn slot_for_mapping_key_phase0(
&self,
ctx: &mut Context<F>,
mapping_slot: SafeBytes32<F>,
key: SolidityType<F>,
) -> MappingWitness<F> {
match key {
SolidityType::Value(key) => {
MappingWitness::Value(self.slot_for_mapping_value_key(ctx, &mapping_slot, &key))
}
SolidityType::NonValue(key) => MappingWitness::NonValue(
self.slot_for_mapping_nonvalue_key_phase0(ctx, mapping_slot, key),
),
}
}
pub fn slot_for_mapping_key_phase1(
&self,
(ctx_gate, ctx_rlc): RlcContextPair<F>,
witness: MappingWitness<F>,
) -> MappingTrace<F> {
match witness {
MappingWitness::Value(_) => None,
MappingWitness::NonValue(witness) => {
Some(self.slot_for_mapping_nonvalue_key_phase1((ctx_gate, ctx_rlc), witness))
}
}
}
pub fn slot_for_nested_mapping_phase0<const MAX_NESTING: usize>(
&self,
ctx: &mut Context<F>,
mapping_slot: SafeBytes32<F>,
keys: [SolidityType<F>; MAX_NESTING],
nestings: AssignedValue<F>,
) -> NestedMappingWitness<F> {
self.range().check_less_than_safe(ctx, nestings, MAX_NESTING as u64 + 1);
let mut witness = Vec::with_capacity(MAX_NESTING);
let mut slots = Vec::with_capacity(MAX_NESTING);
for key in keys {
let m_slot = slots.last().unwrap_or(&mapping_slot).clone();
let w = self.slot_for_mapping_key_phase0(ctx, m_slot.clone(), key);
slots.push(w.slot());
witness.push(w);
}
let slot = if MAX_NESTING == 1 {
slots.pop().unwrap()
} else {
let nestings_minus_one = self.gate().sub(ctx, nestings, Constant(F::ONE));
let indicator = self.gate().idx_to_indicator(ctx, nestings_minus_one, MAX_NESTING);
let slot = self.gate().select_array_by_indicator(ctx, &slots, &indicator);
SafeTypeChip::unsafe_to_safe_type(slot)
};
NestedMappingWitness { witness, slot, nestings }
}
pub fn slot_for_nested_mapping_phase1(
&self,
(ctx_gate, ctx_rlc): RlcContextPair<F>,
witness: NestedMappingWitness<F>,
) -> Vec<MappingTrace<F>> {
witness
.witness
.into_iter()
.map(|w| self.slot_for_mapping_key_phase1((ctx_gate, ctx_rlc), w))
.collect_vec()
}
pub fn verify_mapping_storage_phase0<const MAX_NESTING: usize>(
&self,
ctx: &mut Context<F>,
mapping_slot: SafeBytes32<F>,
keys: [SolidityType<F>; MAX_NESTING],
nestings: AssignedValue<F>,
proof: MPTProof<F>,
) -> (NestedMappingWitness<F>, EthStorageWitness<F>) {
let mapping_witness =
self.slot_for_nested_mapping_phase0(ctx, mapping_slot, keys, nestings);
let storage_witness = {
let storage_chip = EthStorageChip::new(self.mpt, None);
storage_chip.parse_storage_proof_phase0(ctx, mapping_witness.slot.clone(), proof)
};
(mapping_witness, storage_witness)
}
pub fn verify_mapping_storage_phase1(
&self,
(ctx_gate, ctx_rlc): RlcContextPair<F>,
mapping_witness: NestedMappingWitness<F>,
storage_witness: EthStorageWitness<F>,
) -> (Vec<MappingTrace<F>>, EthStorageTrace<F>) {
let mapping_trace =
self.slot_for_nested_mapping_phase1((ctx_gate, ctx_rlc), mapping_witness);
let storage_trace = {
let storage_chip = EthStorageChip::new(self.mpt, None);
storage_chip.parse_storage_proof_phase1((ctx_gate, ctx_rlc), storage_witness)
};
(mapping_trace, storage_trace)
}
}