use core::array;
use miden_core::{
WORD_SIZE,
field::{Algebra, PrimeCharacteristicRing},
};
use crate::{
lookup::{Challenges, message::LookupMessage},
trace::chiplets::hasher::{RATE_LEN, STATE_WIDTH},
};
type SpongeState<E> = [E; STATE_WIDTH];
type Rate<E> = [E; RATE_LEN];
type WordFields<E> = [E; WORD_SIZE];
pub const MIDEN_MAX_MESSAGE_WIDTH: usize = 16;
const _: () = assert!(
MIDEN_MAX_MESSAGE_WIDTH == 16,
"MIDEN_MAX_MESSAGE_WIDTH is hardcoded as 16 by the MASM recursive verifier (4 squarings to reach gamma = beta^16). Update `crates/lib/core/asm/sys/vm/public_inputs.masm` before changing this constant.",
);
#[repr(usize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BusId {
KernelRomInit = 0,
BlockHashTable = 1,
LogDeferredRoot = 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::KernelRomInit as usize == 0);
const _: () = assert!(BusId::BlockHashTable as usize == 1);
const _: () = assert!(BusId::LogDeferredRoot as usize == 2);
const _: () = assert!(BusId::KernelRomCall as usize == 3);
const _: () = assert!(BusId::HasherLinearHashInit as usize == 4);
const _: () = assert!(BusId::HasherReturnState as usize == 5);
const _: () = assert!(BusId::HasherAbsorption as usize == 6);
const _: () = assert!(BusId::HasherReturnHash as usize == 7);
const _: () = assert!(BusId::HasherMerkleVerifyInit as usize == 8);
const _: () = assert!(BusId::HasherMerkleOldInit as usize == 9);
const _: () = assert!(BusId::HasherMerkleNewInit as usize == 10);
const _: () = assert!(BusId::MemoryReadElement as usize == 11);
const _: () = assert!(BusId::MemoryWriteElement as usize == 12);
const _: () = assert!(BusId::MemoryReadWord as usize == 13);
const _: () = assert!(BusId::MemoryWriteWord as usize == 14);
const _: () = assert!(BusId::Bitwise as usize == 15);
const _: () = assert!(BusId::AceInit as usize == 16);
const _: () = assert!(BusId::BlockStackTable as usize == 17);
const _: () = assert!(BusId::OpGroupTable as usize == 18);
const _: () = assert!(BusId::StackOverflowTable as usize == 19);
const _: () = assert!(BusId::SiblingTable as usize == 20);
const _: () = assert!(BusId::RangeCheck as usize == 21);
const _: () = assert!(BusId::AceWiring as usize == 22);
const _: () = assert!(BusId::HasherPermLinkInput as usize == 23);
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(SpongeState<E>),
Rate(Rate<E>),
Word(WordFields<E>),
}
#[derive(Clone, Debug)]
pub(super) struct MerkleInitFromSelectorsMsg<E> {
pub s1: E,
pub s2: E,
pub direction_bit: E,
pub addr: E,
pub node_index: E,
pub rate_0: WordFields<E>,
pub rate_1: WordFields<E>,
}
impl<E: PrimeCharacteristicRing + Clone> HasherMsg<E> {
pub fn linear_hash_init(addr: E, state: SpongeState<E>) -> Self {
Self {
kind: BusId::HasherLinearHashInit,
addr,
node_index: E::ZERO,
payload: HasherPayload::State(state),
}
}
pub fn control_block(addr: E, rate: &Rate<E>, 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: SpongeState<E>) -> Self {
Self {
kind: BusId::HasherReturnState,
addr,
node_index: E::ZERO,
payload: HasherPayload::State(state),
}
}
pub fn absorption(addr: E, rate: Rate<E>) -> Self {
Self {
kind: BusId::HasherAbsorption,
addr,
node_index: E::ZERO,
payload: HasherPayload::Rate(rate),
}
}
pub fn return_hash(addr: E, word: WordFields<E>) -> 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: WordFields<E>) -> Self {
Self {
kind: BusId::HasherMerkleVerifyInit,
addr,
node_index,
payload: HasherPayload::Word(word),
}
}
pub fn merkle_old_init(addr: E, node_index: E, word: WordFields<E>) -> Self {
Self {
kind: BusId::HasherMerkleOldInit,
addr,
node_index,
payload: HasherPayload::Word(word),
}
}
pub fn merkle_new_init(addr: E, node_index: E, word: WordFields<E>) -> Self {
Self {
kind: BusId::HasherMerkleNewInit,
addr,
node_index,
payload: HasherPayload::Word(word),
}
}
}
#[derive(Clone, Debug)]
pub enum MemoryMsg<E> {
#[non_exhaustive]
Element {
bus: BusId,
ctx: E,
addr: E,
clk: E,
element: E,
},
#[non_exhaustive]
Word {
bus: BusId,
ctx: E,
addr: E,
clk: E,
word: WordFields<E>,
},
}
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: WordFields<E>) -> Self {
Self::Word {
bus: BusId::MemoryReadWord,
ctx,
addr,
clk,
word,
}
}
pub fn write_word(ctx: E, addr: E, clk: E, word: WordFields<E>) -> 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: WordFields<E>,
},
}
#[derive(Clone, Debug)]
pub enum BlockHashMsg<E> {
FirstChild {
parent: E,
child_hash: WordFields<E>,
},
Child {
parent: E,
child_hash: WordFields<E>,
},
LoopBody {
parent: E,
child_hash: WordFields<E>,
},
End {
parent: E,
child_hash: WordFields<E>,
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,
}
const HASHER_PERM_LINK_STATE_OFFSET: usize = 2;
const _: () = assert!(HASHER_PERM_LINK_STATE_OFFSET + STATE_WIDTH <= MIDEN_MAX_MESSAGE_WIDTH);
#[derive(Clone, Debug)]
pub enum HasherPermLinkMsg<E> {
Input { perm_id: E, state: SpongeState<E> },
Output { perm_id: E, state: SpongeState<E> },
}
#[derive(Clone, Debug)]
pub struct KernelRomMsg<E> {
bus: BusId,
pub digest: WordFields<E>,
}
impl<E: PrimeCharacteristicRing + Clone> KernelRomMsg<E> {
pub fn call(digest: WordFields<E>) -> Self {
Self { bus: BusId::KernelRomCall, digest }
}
pub fn init(digest: WordFields<E>) -> 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 LogDeferredMsg<E> {
pub state: WordFields<E>,
}
#[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: WordFields<E>,
}
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 MerkleInitFromSelectorsMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
let s1 = self.s1.clone();
let s2 = self.s2.clone();
let not_s1 = E::ONE - s1.clone();
let not_s2 = E::ONE - s2.clone();
let f_mp = not_s1 * s2.clone();
let f_mv = s1.clone() * not_s2;
let f_mu = s1 * s2;
let mut acc = challenges.bus_prefix[BusId::HasherMerkleVerifyInit as usize].clone() * f_mp
+ challenges.bus_prefix[BusId::HasherMerkleOldInit as usize].clone() * f_mv
+ challenges.bus_prefix[BusId::HasherMerkleNewInit as usize].clone() * f_mu;
acc += challenges.inner_product_at(0, &[self.addr.clone(), self.node_index.clone()]);
let bit = self.direction_bit.clone();
let one_minus_bit = E::ONE - bit.clone();
let word: WordFields<E> = array::from_fn(|i| {
self.rate_0[i].clone() * one_minus_bit.clone() + self.rate_1[i].clone() * bit.clone()
});
acc += challenges.inner_product_at(2, &word);
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 LogDeferredMsg<E>
where
E: PrimeCharacteristicRing + Clone,
EF: PrimeCharacteristicRing + Clone + Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
challenges.encode(BusId::LogDeferredRoot as usize, self.state.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, perm_id, state) = match self {
Self::Input { perm_id, state } => (BusId::HasherPermLinkInput, perm_id, state),
Self::Output { perm_id, state } => (BusId::HasherPermLinkOutput, perm_id, state),
};
let mut acc = challenges.bus_prefix[bus as usize].clone();
acc += perm_id.clone();
acc += challenges.inner_product_at(HASHER_PERM_LINK_STATE_OFFSET, state.as_slice());
acc
}
}
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: WordFields<E>,
}
#[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
}
}
#[derive(Clone, Debug)]
pub(super) struct SiblingFromRatesMsg<E> {
pub direction_bit: E,
pub mrupdate_id: E,
pub node_index: E,
pub rate_0: WordFields<E>,
pub rate_1: WordFields<E>,
}
impl<E, EF> LookupMessage<E, EF> for SiblingFromRatesMsg<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 bit = self.direction_bit.clone();
let one_minus_bit = E::ONE - bit.clone();
let selected_rate_0: WordFields<E> =
array::from_fn(|i| self.rate_0[i].clone() * bit.clone());
let selected_rate_1: WordFields<E> =
array::from_fn(|i| self.rate_1[i].clone() * one_minus_bit.clone());
acc += challenges.inner_product_at(3, &selected_rate_0);
acc += challenges.inner_product_at(7, &selected_rate_1);
acc
}
}
#[cfg(test)]
mod tests {
use miden_core::Felt;
use super::{
BusId, HasherMsg, MIDEN_MAX_MESSAGE_WIDTH, MerkleInitFromSelectorsMsg, SiblingBit,
SiblingFromRatesMsg, SiblingMsg,
};
use crate::lookup::{Challenges, message::LookupMessage};
const CHALLENGE_POINTS: [(u64, u64); 3] = [
(29, 31),
(0x0123_4567_89ab_cdef, 0xa5a5_5a5a_0f0f_1111),
(1 << 40, (1 << 50) + 33),
];
fn challenge_points() -> impl Iterator<Item = Challenges<Felt>> {
CHALLENGE_POINTS.into_iter().map(|(alpha, beta)| {
Challenges::new(
Felt::new_unchecked(alpha),
Felt::new_unchecked(beta),
MIDEN_MAX_MESSAGE_WIDTH,
BusId::COUNT,
)
})
}
#[test]
fn sibling_from_rates_matches_selected_sibling_for_boolean_directions() {
let mrupdate_id = Felt::from_u32(37);
let node_index = Felt::from_u32(41);
let rate_0 = [43, 47, 53, 59].map(Felt::from_u32);
let rate_1 = [61, 67, 71, 73].map(Felt::from_u32);
for challenges in challenge_points() {
for (direction_bit, bit, sibling) in
[(Felt::ZERO, SiblingBit::Zero, rate_1), (Felt::ONE, SiblingBit::One, rate_0)]
{
let from_rates = SiblingFromRatesMsg {
direction_bit,
mrupdate_id,
node_index,
rate_0,
rate_1,
};
let selected = SiblingMsg { bit, mrupdate_id, node_index, h: sibling };
assert_eq!(
<SiblingFromRatesMsg<Felt> as LookupMessage<Felt, Felt>>::encode(
&from_rates,
&challenges,
),
<SiblingMsg<Felt> as LookupMessage<Felt, Felt>>::encode(&selected, &challenges,),
);
}
}
}
#[test]
fn merkle_init_from_selectors_matches_typed_messages() {
let addr = Felt::from_u32(37);
let node_index = Felt::from_u32(41);
let rate_0 = [43, 47, 53, 59].map(Felt::from_u32);
let rate_1 = [61, 67, 71, 73].map(Felt::from_u32);
for challenges in challenge_points() {
for (s1, s2, kind) in [
(Felt::ZERO, Felt::ONE, BusId::HasherMerkleVerifyInit),
(Felt::ONE, Felt::ZERO, BusId::HasherMerkleOldInit),
(Felt::ONE, Felt::ONE, BusId::HasherMerkleNewInit),
] {
for direction_bit in [Felt::ZERO, Felt::ONE] {
let from_selectors = MerkleInitFromSelectorsMsg {
s1,
s2,
direction_bit,
addr,
node_index,
rate_0,
rate_1,
};
let word = if direction_bit == Felt::ZERO { rate_0 } else { rate_1 };
let typed = match kind {
BusId::HasherMerkleVerifyInit => {
HasherMsg::merkle_verify_init(addr, node_index, word)
},
BusId::HasherMerkleOldInit => {
HasherMsg::merkle_old_init(addr, node_index, word)
},
BusId::HasherMerkleNewInit => {
HasherMsg::merkle_new_init(addr, node_index, word)
},
_ => unreachable!("test cases contain only Merkle-init bus ids"),
};
assert_eq!(
<MerkleInitFromSelectorsMsg<Felt> as LookupMessage<Felt, Felt>>::encode(
&from_selectors,
&challenges,
),
<HasherMsg<Felt> as LookupMessage<Felt, Felt>>::encode(&typed, &challenges,),
);
}
}
}
}
}