1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
use super::*;
use bus_mapping::state_db::CodeDB;
/// Tag to identify the field in a Bytecode Table row
#[derive(Clone, Copy, Debug)]
pub enum BytecodeFieldTag {
/// Header field
Header,
/// Byte field
Byte,
}
impl_expr!(BytecodeFieldTag);
/// Table with Bytecode indexed by its Code Hash
#[derive(Clone, Debug)]
pub struct BytecodeTable {
/// Code Hash
pub code_hash: WordLoHi<Column<Advice>>,
/// Tag
pub tag: Column<Advice>,
/// Index
pub index: Column<Advice>,
/// Is Code is true when the byte is not an argument to a PUSH* instruction.
pub is_code: Column<Advice>,
/// Value
pub value: Column<Advice>,
}
impl BytecodeTable {
/// Construct a new BytecodeTable
pub fn construct<F: Field>(meta: &mut ConstraintSystem<F>) -> Self {
let [tag, index, is_code, value] = array::from_fn(|_| meta.advice_column());
let code_hash = WordLoHi::new([meta.advice_column(), meta.advice_column()]);
Self {
code_hash,
tag,
index,
is_code,
value,
}
}
/// Assign the `BytecodeTable` from a list of bytecodes, following the same
/// table layout that the Bytecode Circuit uses.
pub fn load<F: Field>(
&self,
layouter: &mut impl Layouter<F>,
bytecodes: CodeDB,
) -> Result<(), Error> {
layouter.assign_region(
|| "bytecode table",
|mut region| {
let mut offset = 0;
for column in <BytecodeTable as LookupTable<F>>::advice_columns(self) {
region.assign_advice(
|| "bytecode table all-zero row",
column,
offset,
|| Value::known(F::ZERO),
)?;
}
offset += 1;
let bytecode_table_columns =
<BytecodeTable as LookupTable<F>>::advice_columns(self);
for bytecode in bytecodes.clone().into_iter() {
let rows = {
let code_hash = WordLoHi::from(bytecode.hash());
std::iter::once([
code_hash.lo(),
code_hash.hi(),
F::from(BytecodeFieldTag::Header as u64),
F::ZERO,
F::ZERO,
F::from(bytecode.codesize() as u64),
])
.chain(bytecode.code_vec().iter().enumerate().map(
|(index, &(byte, is_code))| {
[
code_hash.lo(),
code_hash.hi(),
F::from(BytecodeFieldTag::Byte as u64),
F::from(index as u64),
F::from(is_code.into()),
F::from(byte.into()),
]
},
))
.collect_vec()
};
for row in rows.iter() {
for (&column, value) in bytecode_table_columns.iter().zip_eq(row.to_vec()) {
region.assign_advice(
|| format!("bytecode table row {}", offset),
column,
offset,
|| Value::known(value),
)?;
}
offset += 1;
}
}
Ok(())
},
)
}
}
impl<F: Field> LookupTable<F> for BytecodeTable {
fn columns(&self) -> Vec<Column<Any>> {
vec![
self.code_hash.lo().into(),
self.code_hash.hi().into(),
self.tag.into(),
self.index.into(),
self.is_code.into(),
self.value.into(),
]
}
fn annotations(&self) -> Vec<String> {
vec![
String::from("code_hash_lo"),
String::from("code_hash_hi"),
String::from("tag"),
String::from("index"),
String::from("is_code"),
String::from("value"),
]
}
}