use miden_core::field::{Algebra, PrimeCharacteristicRing};
use crate::lookup::Challenges;
pub const MIDEN_MAX_MESSAGE_WIDTH: usize = 16;
#[repr(usize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BusId {
KernelRomInit = 0,
BlockHashTable = 1,
LogPrecompileTranscript = 2,
KernelRomCall = 3,
HasherLinearHashInit = 4,
HasherReturnState = 5,
HasherAbsorption = 6,
HasherReturnHash = 7,
HasherMerkleVerifyInit = 8,
HasherMerkleOldInit = 9,
HasherMerkleNewInit = 10,
MemoryReadElement = 11,
MemoryWriteElement = 12,
MemoryReadWord = 13,
MemoryWriteWord = 14,
Bitwise = 15,
AceInit = 16,
BlockStackTable = 17,
OpGroupTable = 18,
StackOverflowTable = 19,
SiblingTable = 20,
RangeCheck = 21,
AceWiring = 22,
HasherPermLinkInput = 23,
HasherPermLinkOutput = 24,
}
impl BusId {
pub const COUNT: usize = Self::HasherPermLinkOutput as usize + 1;
}
const _: () = assert!(BusId::HasherPermLinkOutput as usize == 24);
#[derive(Clone, Debug)]
pub struct HasherMsg<E> {
pub kind: BusId,
pub addr: E,
pub node_index: E,
pub payload: HasherPayload<E>,
}
#[derive(Clone, Debug)]
pub enum HasherPayload<E> {
State([E; 12]),
Rate([E; 8]),
Word([E; 4]),
}
impl<E: PrimeCharacteristicRing + Clone> HasherMsg<E> {
pub fn linear_hash_init(addr: E, state: [E; 12]) -> Self {
Self {
kind: BusId::HasherLinearHashInit,
addr,
node_index: E::ZERO,
payload: HasherPayload::State(state),
}
}
pub fn control_block(addr: E, rate: &[E; 8], opcode: u8) -> Self {
let state = [
rate[0].clone(),
rate[1].clone(),
rate[2].clone(),
rate[3].clone(),
rate[4].clone(),
rate[5].clone(),
rate[6].clone(),
rate[7].clone(),
E::ZERO,
E::from_u16(opcode as u16),
E::ZERO,
E::ZERO,
];
Self {
kind: BusId::HasherLinearHashInit,
addr,
node_index: E::ZERO,
payload: HasherPayload::State(state),
}
}
pub fn return_state(addr: E, state: [E; 12]) -> Self {
Self {
kind: BusId::HasherReturnState,
addr,
node_index: E::ZERO,
payload: HasherPayload::State(state),
}
}
pub fn absorption(addr: E, rate: [E; 8]) -> Self {
Self {
kind: BusId::HasherAbsorption,
addr,
node_index: E::ZERO,
payload: HasherPayload::Rate(rate),
}
}
pub fn return_hash(addr: E, word: [E; 4]) -> Self {
Self {
kind: BusId::HasherReturnHash,
addr,
node_index: E::ZERO,
payload: HasherPayload::Word(word),
}
}
pub fn merkle_verify_init(addr: E, node_index: E, word: [E; 4]) -> Self {
Self {
kind: BusId::HasherMerkleVerifyInit,
addr,
node_index,
payload: HasherPayload::Word(word),
}
}
pub fn merkle_old_init(addr: E, node_index: E, word: [E; 4]) -> Self {
Self {
kind: BusId::HasherMerkleOldInit,
addr,
node_index,
payload: HasherPayload::Word(word),
}
}
pub fn merkle_new_init(addr: E, node_index: E, word: [E; 4]) -> Self {
Self {
kind: BusId::HasherMerkleNewInit,
addr,
node_index,
payload: HasherPayload::Word(word),
}
}
}
#[derive(Clone, Debug)]
pub enum MemoryMsg<E> {
Element {
bus: BusId,
ctx: E,
addr: E,
clk: E,
element: E,
},
Word {
bus: BusId,
ctx: E,
addr: E,
clk: E,
word: [E; 4],
},
}
impl<E> MemoryMsg<E> {
pub fn read_element(ctx: E, addr: E, clk: E, element: E) -> Self {
Self::Element {
bus: BusId::MemoryReadElement,
ctx,
addr,
clk,
element,
}
}
pub fn write_element(ctx: E, addr: E, clk: E, element: E) -> Self {
Self::Element {
bus: BusId::MemoryWriteElement,
ctx,
addr,
clk,
element,
}
}
pub fn read_word(ctx: E, addr: E, clk: E, word: [E; 4]) -> Self {
Self::Word {
bus: BusId::MemoryReadWord,
ctx,
addr,
clk,
word,
}
}
pub fn write_word(ctx: E, addr: E, clk: E, word: [E; 4]) -> Self {
Self::Word {
bus: BusId::MemoryWriteWord,
ctx,
addr,
clk,
word,
}
}
}
#[derive(Clone, Debug)]
pub struct BitwiseMsg<E> {
pub op: E,
pub a: E,
pub b: E,
pub result: E,
}
impl<E: PrimeCharacteristicRing> BitwiseMsg<E> {
const AND_SELECTOR: u32 = 0;
const XOR_SELECTOR: u32 = 1;
pub fn and(a: E, b: E, result: E) -> Self {
Self {
op: E::from_u32(Self::AND_SELECTOR),
a,
b,
result,
}
}
pub fn xor(a: E, b: E, result: E) -> Self {
Self {
op: E::from_u32(Self::XOR_SELECTOR),
a,
b,
result,
}
}
}
#[derive(Clone, Debug)]
pub enum BlockStackMsg<E> {
Simple {
block_id: E,
parent_id: E,
is_loop: E,
},
Full {
block_id: E,
parent_id: E,
is_loop: E,
ctx: E,
fmp: E,
depth: E,
fn_hash: [E; 4],
},
}
#[derive(Clone, Debug)]
pub enum BlockHashMsg<E> {
FirstChild {
parent: E,
child_hash: [E; 4],
},
Child {
parent: E,
child_hash: [E; 4],
},
LoopBody {
parent: E,
child_hash: [E; 4],
},
End {
parent: E,
child_hash: [E; 4],
is_first_child: E,
is_loop_body: E,
},
}
#[derive(Clone, Debug)]
pub struct OpGroupMsg<E> {
pub batch_id: E,
pub group_pos: E,
pub group_value: E,
}
impl<E: PrimeCharacteristicRing + Clone> OpGroupMsg<E> {
pub fn new<V>(batch_id: &E, group_count: V, offset: u16, group_value: E) -> Self
where
V: core::ops::Sub<E, Output = E> + Clone,
{
Self {
batch_id: batch_id.clone(),
group_pos: group_count - E::from_u16(offset),
group_value,
}
}
}
#[derive(Clone, Debug)]
pub struct StackOverflowMsg<E> {
pub clk: E,
pub val: E,
pub prev: E,
}
#[derive(Clone, Debug)]
pub enum HasherPermLinkMsg<E> {
Input { state: [E; 12] },
Output { state: [E; 12] },
}
#[derive(Clone, Debug)]
pub struct KernelRomMsg<E> {
bus: BusId,
pub digest: [E; 4],
}
impl<E: PrimeCharacteristicRing + Clone> KernelRomMsg<E> {
pub fn call(digest: [E; 4]) -> Self {
Self { bus: BusId::KernelRomCall, digest }
}
pub fn init(digest: [E; 4]) -> Self {
Self { bus: BusId::KernelRomInit, digest }
}
}
#[derive(Clone, Debug)]
pub struct AceInitMsg<E> {
pub clk: E,
pub ctx: E,
pub ptr: E,
pub num_read: E,
pub num_eval: E,
}
#[derive(Clone, Debug)]
pub struct RangeMsg<E> {
pub value: E,
}
#[derive(Clone, Debug)]
pub struct LogCapacityMsg<E> {
pub capacity: [E; 4],
}
#[derive(Clone, Debug)]
pub struct AceWireMsg<E> {
pub clk: E,
pub ctx: E,
pub id: E,
pub v0: E,
pub v1: E,
}
#[derive(Clone, Debug)]
pub struct MemoryResponseMsg<E> {
pub is_read: E,
pub ctx: E,
pub addr: E,
pub clk: E,
pub is_word: E,
pub element: E,
pub word: [E; 4],
}
use crate::lookup::message::LookupMessage;
impl<E, EF> LookupMessage<E, EF> for HasherMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
let mut acc = challenges.bus_prefix[self.kind as usize].clone();
acc += challenges.inner_product_at(0, &[self.addr.clone(), self.node_index.clone()]);
let payload = match &self.payload {
HasherPayload::State(state) => state.as_slice(),
HasherPayload::Rate(rate) => rate.as_slice(),
HasherPayload::Word(word) => word.as_slice(),
};
acc += challenges.inner_product_at(2, payload);
acc
}
}
impl<E, EF> LookupMessage<E, EF> for MemoryMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
let bus = match self {
Self::Element { bus, .. } | Self::Word { bus, .. } => *bus as usize,
};
let mut acc = challenges.bus_prefix[bus].clone();
match self {
Self::Element { ctx, addr, clk, element, .. } => {
acc += challenges.inner_product_at(
0,
&[ctx.clone(), addr.clone(), clk.clone(), element.clone()],
);
},
Self::Word { ctx, addr, clk, word, .. } => {
acc += challenges.inner_product_at(0, &[ctx.clone(), addr.clone(), clk.clone()]);
acc += challenges.inner_product_at(3, word.as_slice());
},
}
acc
}
}
impl<E, EF> LookupMessage<E, EF> for BitwiseMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
challenges.encode(
BusId::Bitwise as usize,
[self.op.clone(), self.a.clone(), self.b.clone(), self.result.clone()],
)
}
}
impl<E, EF> LookupMessage<E, EF> for BlockStackMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
let mut acc = challenges.bus_prefix[BusId::BlockStackTable as usize].clone();
match self {
Self::Simple { block_id, parent_id, is_loop } => {
acc += challenges
.inner_product_at(0, &[block_id.clone(), parent_id.clone(), is_loop.clone()]);
},
Self::Full {
block_id,
parent_id,
is_loop,
ctx,
fmp,
depth,
fn_hash,
} => {
acc += challenges.inner_product_at(
0,
&[
block_id.clone(),
parent_id.clone(),
is_loop.clone(),
ctx.clone(),
fmp.clone(),
depth.clone(),
],
);
acc += challenges.inner_product_at(6, fn_hash.as_slice());
},
}
acc
}
}
impl<E, EF> LookupMessage<E, EF> for BlockHashMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
let (parent, child_hash, is_first_child, is_loop_body) = match self {
Self::FirstChild { parent, child_hash } => (parent, child_hash, E::ONE, E::ZERO),
Self::Child { parent, child_hash } => (parent, child_hash, E::ZERO, E::ZERO),
Self::LoopBody { parent, child_hash } => (parent, child_hash, E::ZERO, E::ONE),
Self::End {
parent,
child_hash,
is_first_child,
is_loop_body,
} => (parent, child_hash, is_first_child.clone(), is_loop_body.clone()),
};
challenges.encode(
BusId::BlockHashTable as usize,
[
child_hash[0].clone(),
child_hash[1].clone(),
child_hash[2].clone(),
child_hash[3].clone(),
parent.clone(),
is_first_child,
is_loop_body,
],
)
}
}
impl<E, EF> LookupMessage<E, EF> for OpGroupMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
challenges.encode(
BusId::OpGroupTable as usize,
[self.batch_id.clone(), self.group_pos.clone(), self.group_value.clone()],
)
}
}
impl<E, EF> LookupMessage<E, EF> for StackOverflowMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
challenges.encode(
BusId::StackOverflowTable as usize,
[self.clk.clone(), self.val.clone(), self.prev.clone()],
)
}
}
impl<E, EF> LookupMessage<E, EF> for KernelRomMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
challenges.encode(self.bus as usize, self.digest.clone())
}
}
impl<E, EF> LookupMessage<E, EF> for AceInitMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
challenges.encode(
BusId::AceInit as usize,
[
self.clk.clone(),
self.ctx.clone(),
self.ptr.clone(),
self.num_read.clone(),
self.num_eval.clone(),
],
)
}
}
impl<E, EF> LookupMessage<E, EF> for RangeMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
challenges.encode(BusId::RangeCheck as usize, [self.value.clone()])
}
}
impl<E, EF> LookupMessage<E, EF> for LogCapacityMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
challenges.encode(BusId::LogPrecompileTranscript as usize, self.capacity.clone())
}
}
impl<E, EF> LookupMessage<E, EF> for HasherPermLinkMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
let (bus, state) = match self {
Self::Input { state } => (BusId::HasherPermLinkInput, state),
Self::Output { state } => (BusId::HasherPermLinkOutput, state),
};
challenges.encode(bus as usize, state.clone())
}
}
impl<E, EF> LookupMessage<E, EF> for AceWireMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
challenges.encode(
BusId::AceWiring as usize,
[
self.clk.clone(),
self.ctx.clone(),
self.id.clone(),
self.v0.clone(),
self.v1.clone(),
],
)
}
}
impl<E, EF> LookupMessage<E, EF> for MemoryResponseMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
let bp = &challenges.beta_powers;
let is_read = self.is_read.clone();
let is_write: E = E::ONE - is_read.clone();
let is_word = self.is_word.clone();
let is_element: E = E::ONE - is_word.clone();
let prefix_element = challenges.bus_prefix[BusId::MemoryReadElement as usize].clone()
* is_read.clone()
+ challenges.bus_prefix[BusId::MemoryWriteElement as usize].clone() * is_write.clone();
let prefix_word = challenges.bus_prefix[BusId::MemoryReadWord as usize].clone() * is_read
+ challenges.bus_prefix[BusId::MemoryWriteWord as usize].clone() * is_write;
let prefix = prefix_element * is_element.clone() + prefix_word * is_word.clone();
let mut acc = prefix;
acc += bp[0].clone() * self.ctx.clone();
acc += bp[1].clone() * self.addr.clone();
acc += bp[2].clone() * self.clk.clone();
acc += bp[3].clone() * self.element.clone() * is_element;
acc += challenges.inner_product_at(3, self.word.as_slice()) * is_word;
acc
}
}
#[derive(Clone, Debug)]
pub struct SiblingMsg<E> {
pub bit: SiblingBit,
pub mrupdate_id: E,
pub node_index: E,
pub h: [E; 4],
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SiblingBit {
Zero,
One,
}
impl<E, EF> LookupMessage<E, EF> for SiblingMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
let mut acc = challenges.bus_prefix[BusId::SiblingTable as usize].clone();
acc += challenges.inner_product_at(1, &[self.mrupdate_id.clone(), self.node_index.clone()]);
let base = match self.bit {
SiblingBit::Zero => 7,
SiblingBit::One => 3,
};
acc += challenges.inner_product_at(base, self.h.as_slice());
acc
}
}