Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bring in Poseidon implementation under cdk_erigon feature flag #577

Merged
merged 3 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ LOGIC_CIRCUIT_SIZE=4..21
MEMORY_CIRCUIT_SIZE=17..24
MEMORY_BEFORE_CIRCUIT_SIZE=16..23
MEMORY_AFTER_CIRCUIT_SIZE=7..23
POSEIDON_CIRCUIT_SIZE=4..25
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -239,5 +239,8 @@ jobs:
- name: Run cargo clippy
run: cargo clippy --all-features --all-targets -- -D warnings -A incomplete-features

- name: Run cargo clippy (with `cdk_erigon` flag)
run: cargo clippy --all-features --all-targets --features cdk_erigon -- -D warnings -A incomplete-features

- name: Rustdoc
run: cargo doc --all
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion evm_arithmetization/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ serde-big-array = { workspace = true }

# Local dependencies
mpt_trie = { workspace = true }
smt_trie = { workspace = true, optional = true }
zk_evm_proc_macro = { workspace = true }

[dev-dependencies]
Expand All @@ -64,7 +65,7 @@ parallel = [
"starky/parallel",
]
polygon_pos = []
cdk_erigon = []
cdk_erigon = ["smt_trie"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But what does the feature mean?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That we're targeting a cdk-based network, i.e like the current type2 zkEVM from Polygon Hermez, with the difference that we're a true type 2 (they have a custom set of opcodes).
We called the feature that way because the reference implementation is cdk_erigon.


[[bin]]
name = "assemble"
Expand Down
75 changes: 71 additions & 4 deletions evm_arithmetization/src/all_stark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ use crate::logic::LogicStark;
use crate::memory::memory_stark::MemoryStark;
use crate::memory::memory_stark::{self, ctl_context_pruning_looking};
use crate::memory_continuation::memory_continuation_stark::{self, MemoryContinuationStark};
#[cfg(feature = "cdk_erigon")]
use crate::poseidon::{
columns::POSEIDON_SPONGE_RATE,
poseidon_stark::{self, PoseidonStark, FELT_MAX_BYTES},
};

/// Structure containing all STARKs and the cross-table lookups.
#[derive(Clone)]
Expand All @@ -38,6 +43,8 @@ pub struct AllStark<F: RichField + Extendable<D>, const D: usize> {
pub(crate) memory_stark: MemoryStark<F, D>,
pub(crate) mem_before_stark: MemoryContinuationStark<F, D>,
pub(crate) mem_after_stark: MemoryContinuationStark<F, D>,
#[cfg(feature = "cdk_erigon")]
pub(crate) poseidon_stark: PoseidonStark<F, D>,
pub(crate) cross_table_lookups: Vec<CrossTableLookup<F>>,
}

Expand All @@ -55,6 +62,8 @@ impl<F: RichField + Extendable<D>, const D: usize> Default for AllStark<F, D> {
memory_stark: MemoryStark::default(),
mem_before_stark: MemoryContinuationStark::default(),
mem_after_stark: MemoryContinuationStark::default(),
#[cfg(feature = "cdk_erigon")]
poseidon_stark: PoseidonStark::default(),
cross_table_lookups: all_cross_table_lookups(),
}
}
Expand All @@ -72,6 +81,8 @@ impl<F: RichField + Extendable<D>, const D: usize> AllStark<F, D> {
self.memory_stark.num_lookup_helper_columns(config),
self.mem_before_stark.num_lookup_helper_columns(config),
self.mem_after_stark.num_lookup_helper_columns(config),
#[cfg(feature = "cdk_erigon")]
self.poseidon_stark.num_lookup_helper_columns(config),
]
}
}
Expand All @@ -90,6 +101,8 @@ pub enum Table {
Memory = 6,
MemBefore = 7,
MemAfter = 8,
#[cfg(feature = "cdk_erigon")]
Poseidon = 9,
}

impl Deref for Table {
Expand All @@ -98,12 +111,20 @@ impl Deref for Table {
fn deref(&self) -> &Self::Target {
// Hacky way to implement `Deref` for `Table` so that we don't have to
// call `Table::Foo as usize`, but perhaps too ugly to be worth it.
[&0, &1, &2, &3, &4, &5, &6, &7, &8][*self as TableIdx]
#[cfg(not(feature = "cdk_erigon"))]
return [&0, &1, &2, &3, &4, &5, &6, &7, &8][*self as TableIdx];

#[cfg(feature = "cdk_erigon")]
[&0, &1, &2, &3, &4, &5, &6, &7, &8, &9][*self as TableIdx]
}
}

/// Number of STARK tables.
pub(crate) const NUM_TABLES: usize = Table::MemAfter as usize + 1;
pub const NUM_TABLES: usize = if cfg!(feature = "cdk_erigon") {
Table::MemAfter as usize + 2
} else {
Table::MemAfter as usize + 1
};

impl Table {
/// Returns all STARK table indices.
Expand All @@ -118,6 +139,8 @@ impl Table {
Self::Memory,
Self::MemBefore,
Self::MemAfter,
#[cfg(feature = "cdk_erigon")]
Self::Poseidon,
]
}
}
Expand All @@ -135,6 +158,12 @@ pub(crate) fn all_cross_table_lookups<F: Field>() -> Vec<CrossTableLookup<F>> {
ctl_mem_before(),
ctl_mem_after(),
ctl_context_pruning(),
#[cfg(feature = "cdk_erigon")]
ctl_poseidon_simple(),
#[cfg(feature = "cdk_erigon")]
ctl_poseidon_general_input(),
#[cfg(feature = "cdk_erigon")]
ctl_poseidon_general_output(),
]
}

Expand Down Expand Up @@ -307,6 +336,16 @@ fn ctl_memory<F: Field>() -> CrossTableLookup<F> {
memory_continuation_stark::ctl_data_memory(),
memory_continuation_stark::ctl_filter(),
);

#[cfg(feature = "cdk_erigon")]
let poseidon_general_reads = (0..FELT_MAX_BYTES * POSEIDON_SPONGE_RATE).map(|i| {
TableWithColumns::new(
*Table::Poseidon,
poseidon_stark::ctl_looking_memory(i),
poseidon_stark::ctl_looking_memory_filter(),
)
});

let all_lookers = vec![
cpu_memory_code_read,
cpu_push_write_ops,
Expand All @@ -317,8 +356,12 @@ fn ctl_memory<F: Field>() -> CrossTableLookup<F> {
.chain(cpu_memory_gp_ops)
.chain(keccak_sponge_reads)
.chain(byte_packing_ops)
.chain(iter::once(mem_before_ops))
.collect();
.chain(iter::once(mem_before_ops));

#[cfg(feature = "cdk_erigon")]
let all_lookers = all_lookers.chain(poseidon_general_reads);

let all_lookers = all_lookers.collect();
let memory_looked = TableWithColumns::new(
*Table::Memory,
memory_stark::ctl_data(),
Expand Down Expand Up @@ -368,3 +411,27 @@ fn ctl_mem_after<F: Field>() -> CrossTableLookup<F> {
);
CrossTableLookup::new(all_lookers, mem_after_looked)
}

#[cfg(feature = "cdk_erigon")]
fn ctl_poseidon_simple<F: Field>() -> CrossTableLookup<F> {
CrossTableLookup::new(
vec![cpu_stark::ctl_poseidon_simple_op()],
poseidon_stark::ctl_looked_simple_op(),
)
}

#[cfg(feature = "cdk_erigon")]
fn ctl_poseidon_general_input<F: Field>() -> CrossTableLookup<F> {
CrossTableLookup::new(
vec![cpu_stark::ctl_poseidon_general_input()],
poseidon_stark::ctl_looked_general_input(),
)
}

#[cfg(feature = "cdk_erigon")]
fn ctl_poseidon_general_output<F: Field>() -> CrossTableLookup<F> {
CrossTableLookup::new(
vec![cpu_stark::ctl_poseidon_general_output()],
poseidon_stark::ctl_looked_general_output(),
)
}
3 changes: 3 additions & 0 deletions evm_arithmetization/src/cpu/columns/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ pub(crate) struct OpsColumnsView<T: Copy> {
pub shift: T,
/// Combines JUMPDEST and KECCAK_GENERAL flags.
pub jumpdest_keccak_general: T,
/// Combines POSEIDON and POSEIDON_GENERAL flags.
#[cfg(feature = "cdk_erigon")]
pub poseidon: T,
/// Combines JUMP and JUMPI flags.
pub jumps: T,
/// Combines PUSH and PROVER_INPUT flags.
Expand Down
2 changes: 2 additions & 0 deletions evm_arithmetization/src/cpu/contextops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ const KEEPS_CONTEXT: OpsColumnsView<bool> = OpsColumnsView {
not_pop: true,
shift: true,
jumpdest_keccak_general: true,
#[cfg(feature = "cdk_erigon")]
poseidon: true,
push_prover_input: true,
jumps: true,
pc_push0: true,
Expand Down
6 changes: 5 additions & 1 deletion evm_arithmetization/src/cpu/control_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsume
use crate::cpu::columns::{CpuColumnsView, COL_MAP};
use crate::cpu::kernel::aggregator::KERNEL;

const NATIVE_INSTRUCTIONS: [usize; 12] = [
const NATIVE_INST_LEN: usize = if cfg!(feature = "cdk_erigon") { 13 } else { 12 };

const NATIVE_INSTRUCTIONS: [usize; NATIVE_INST_LEN] = [
COL_MAP.op.binary_op,
COL_MAP.op.ternary_op,
COL_MAP.op.fp254_op,
Expand All @@ -17,6 +19,8 @@ const NATIVE_INSTRUCTIONS: [usize; 12] = [
COL_MAP.op.not_pop,
COL_MAP.op.shift,
COL_MAP.op.jumpdest_keccak_general,
#[cfg(feature = "cdk_erigon")]
COL_MAP.op.poseidon,
// Not PROVER_INPUT: it is dealt with manually below.
// not JUMPS (possible need to jump)
COL_MAP.op.pc_push0,
Expand Down
81 changes: 81 additions & 0 deletions evm_arithmetization/src/cpu/cpu_stark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,87 @@ pub(crate) fn ctl_filter_set_context<F: Field>() -> Filter<F> {
)
}

#[cfg(feature = "cdk_erigon")]
/// Returns the `TableWithColumns` for the CPU rows calling POSEIDON.
pub(crate) fn ctl_poseidon_simple_op<F: Field>() -> TableWithColumns<F> {
Nashtare marked this conversation as resolved.
Show resolved Hide resolved
// When executing POSEIDON, the GP memory channels are used as follows:
// GP channel 0: stack[-1] = x
// GP channel 1: stack[-2] = y
// GP channel 2: stack[-3] = z
// Such that we can compute `POSEIDON(x || y || z)`.
let mut columns = Vec::new();
for channel in 0..3 {
for i in 0..VALUE_LIMBS / 2 {
columns.push(Column::linear_combination([
(COL_MAP.mem_channels[channel].value[2 * i], F::ONE),
(
COL_MAP.mem_channels[channel].value[2 * i + 1],
F::from_canonical_u64(1 << 32),
),
]));
}
}
columns.extend(Column::singles_next_row(COL_MAP.mem_channels[0].value));
TableWithColumns::new(*Table::Cpu, columns, ctl_poseidon_simple_filter())
}

#[cfg(feature = "cdk_erigon")]
pub(crate) fn ctl_poseidon_general_input<F: Field>() -> TableWithColumns<F> {
// When executing POSEIDON_GENERAL, the GP memory channels are used as follows:
// GP channel 0: stack[-1] = addr (context, segment, virt)
// GP channel 1: stack[-2] = len
let (context, segment, virt) = get_addr(&COL_MAP, 0);
let context = Column::single(context);
let segment: Column<F> = Column::single(segment);
let virt = Column::single(virt);
let len = Column::single(COL_MAP.mem_channels[1].value[0]);

let num_channels = F::from_canonical_usize(NUM_CHANNELS);
let timestamp = Column::linear_combination([(COL_MAP.clock, num_channels)]);

TableWithColumns::new(
*Table::Cpu,
vec![context, segment, virt, len, timestamp],
ctl_poseidon_general_filter(),
)
}

#[cfg(feature = "cdk_erigon")]
/// CTL filter for the `POSEIDON` operation.
/// POSEIDON is differentiated from POSEIDON_GENERAL by its first bit set to 0.
pub(crate) fn ctl_poseidon_simple_filter<F: Field>() -> Filter<F> {
Nashtare marked this conversation as resolved.
Show resolved Hide resolved
Filter::new(
vec![(
Column::single(COL_MAP.op.poseidon),
Column::linear_combination_with_constant([(COL_MAP.opcode_bits[0], -F::ONE)], F::ONE),
)],
vec![],
)
}

#[cfg(feature = "cdk_erigon")]
/// CTL filter for the `POSEIDON_GENERAL` operation.
/// POSEIDON_GENERAL is differentiated from POSEIDON by its first bit set to 1.
pub(crate) fn ctl_poseidon_general_filter<F: Field>() -> Filter<F> {
Nashtare marked this conversation as resolved.
Show resolved Hide resolved
Filter::new(
vec![(
Column::single(COL_MAP.op.poseidon),
Column::single(COL_MAP.opcode_bits[0]),
)],
vec![],
)
}

#[cfg(feature = "cdk_erigon")]
/// Returns the `TableWithColumns` for the CPU rows calling POSEIDON_GENERAL.
pub(crate) fn ctl_poseidon_general_output<F: Field>() -> TableWithColumns<F> {
let mut columns = Vec::new();
columns.extend(Column::singles_next_row(COL_MAP.mem_channels[0].value));
let num_channels = F::from_canonical_usize(NUM_CHANNELS);
columns.push(Column::linear_combination([(COL_MAP.clock, num_channels)]));
TableWithColumns::new(*Table::Cpu, columns, ctl_poseidon_general_filter())
}

/// Disable the specified memory channels.
/// Since channel 0 contains the top of the stack and is handled specially,
/// channels to disable are 1, 2 or both. All cases can be expressed as a vec.
Expand Down
6 changes: 5 additions & 1 deletion evm_arithmetization/src/cpu/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsume

use crate::cpu::columns::{CpuColumnsView, COL_MAP};

const OPCODES_LEN: usize = if cfg!(feature = "cdk_erigon") { 6 } else { 5 };

/// List of opcode blocks
/// Each block corresponds to exactly one flag, and each flag corresponds to
/// exactly one block. Each block of opcodes:
Expand All @@ -29,13 +31,15 @@ use crate::cpu::columns::{CpuColumnsView, COL_MAP};
/// Note: invalid opcodes are not represented here. _Any_ opcode is permitted to
/// decode to `is_invalid`. The kernel then verifies that the opcode was
/// _actually_ invalid.
const OPCODES: [(u8, usize, bool, usize); 5] = [
const OPCODES: [(u8, usize, bool, usize); OPCODES_LEN] = [
// (start index of block, number of top bits to check (log2), kernel-only, flag column)
// ADD, MUL, SUB, DIV, MOD, LT, GT and BYTE flags are handled partly manually here, and partly
// through the Arithmetic table CTL. ADDMOD, MULMOD and SUBMOD flags are handled partly
// manually here, and partly through the Arithmetic table CTL. FP254 operation flags are
// handled partly manually here, and partly through the Arithmetic table CTL.
(0x14, 1, false, COL_MAP.op.eq_iszero),
#[cfg(feature = "cdk_erigon")]
(0x22, 1, true, COL_MAP.op.poseidon),
// AND, OR and XOR flags are handled partly manually here, and partly through the Logic table
// CTL. NOT and POP are handled manually here.
// SHL and SHR flags are handled partly manually here, and partly through the Logic table CTL.
Expand Down
6 changes: 4 additions & 2 deletions evm_arithmetization/src/cpu/gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ const SIMPLE_OPCODES: OpsColumnsView<Option<u32>> = OpsColumnsView {
not_pop: None, // This is handled manually below
shift: G_VERYLOW,
jumpdest_keccak_general: None, // This is handled manually below.
push_prover_input: None, // This is handled manually below.
jumps: None, // Combined flag handled separately.
#[cfg(feature = "cdk_erigon")]
poseidon: KERNEL_ONLY_INSTR,
push_prover_input: None, // This is handled manually below.
jumps: None, // Combined flag handled separately.
pc_push0: G_BASE,
dup_swap: G_VERYLOW,
context_op: KERNEL_ONLY_INSTR,
Expand Down
Loading
Loading