use super::{
super::{
ExecutionOptions, ExecutionTrace, Felt, Kernel, Operation, Process, StackInputs, Word,
},
build_op_group,
};
use crate::DefaultHost;
use alloc::vec::Vec;
use miden_air::trace::{
decoder::{
ADDR_COL_IDX, GROUP_COUNT_COL_IDX, HASHER_STATE_RANGE, IN_SPAN_COL_IDX, NUM_HASHER_COLUMNS,
NUM_OP_BATCH_FLAGS, NUM_OP_BITS, OP_BATCH_1_GROUPS, OP_BATCH_2_GROUPS, OP_BATCH_4_GROUPS,
OP_BATCH_8_GROUPS, OP_BATCH_FLAGS_RANGE, OP_BITS_EXTRA_COLS_RANGE, OP_BITS_OFFSET,
OP_INDEX_COL_IDX,
},
CTX_COL_IDX, DECODER_TRACE_RANGE, DECODER_TRACE_WIDTH, FMP_COL_IDX, FN_HASH_RANGE,
IN_SYSCALL_COL_IDX, SYS_TRACE_RANGE, SYS_TRACE_WIDTH,
};
use test_utils::rand::rand_value;
use vm_core::{
code_blocks::{CodeBlock, Span, OP_BATCH_SIZE},
CodeBlockTable, EMPTY_WORD, ONE, ZERO,
};
const TWO: Felt = Felt::new(2);
const THREE: Felt = Felt::new(3);
const EIGHT: Felt = Felt::new(8);
const INIT_ADDR: Felt = ONE;
const FMP_MIN: Felt = Felt::new(crate::FMP_MIN);
const SYSCALL_FMP_MIN: Felt = Felt::new(crate::SYSCALL_FMP_MIN as u64);
type SystemTrace = [Vec<Felt>; SYS_TRACE_WIDTH];
type DecoderTrace = [Vec<Felt>; DECODER_TRACE_WIDTH];
#[test]
fn span_block_one_group() {
let ops = vec![Operation::Pad, Operation::Add, Operation::Mul];
let span = Span::new(ops.clone());
let program = CodeBlock::new_span(ops.clone());
let (trace, trace_len) = build_trace(&[], &program);
check_op_decoding(&trace, 0, ZERO, Operation::Span, 1, 0, 0);
check_op_decoding(&trace, 1, INIT_ADDR, Operation::Pad, 0, 0, 1);
check_op_decoding(&trace, 2, INIT_ADDR, Operation::Add, 0, 1, 1);
check_op_decoding(&trace, 3, INIT_ADDR, Operation::Mul, 0, 2, 1);
check_op_decoding(&trace, 4, INIT_ADDR, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 5, ZERO, Operation::Halt, 0, 0, 0);
let program_hash: Word = program.hash().into();
check_hasher_state(
&trace,
vec![
span.op_batches()[0].groups().to_vec(), vec![build_op_group(&ops[1..])],
vec![build_op_group(&ops[2..])],
vec![],
program_hash.to_vec(), ],
);
for i in 6..trace_len {
assert!(contains_op(&trace, i, Operation::Halt));
assert_eq!(ZERO, trace[OP_BITS_EXTRA_COLS_RANGE.start][i]);
assert_eq!(ONE, trace[OP_BITS_EXTRA_COLS_RANGE.start + 1][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
}
#[test]
fn span_block_small() {
let iv = [ONE, TWO];
let ops = vec![Operation::Push(iv[0]), Operation::Push(iv[1]), Operation::Add];
let span = Span::new(ops.clone());
let program = CodeBlock::new_span(ops.clone());
let (trace, trace_len) = build_trace(&[], &program);
check_op_decoding(&trace, 0, ZERO, Operation::Span, 4, 0, 0);
check_op_decoding(&trace, 1, INIT_ADDR, Operation::Push(iv[0]), 3, 0, 1);
check_op_decoding(&trace, 2, INIT_ADDR, Operation::Push(iv[1]), 2, 1, 1);
check_op_decoding(&trace, 3, INIT_ADDR, Operation::Add, 1, 2, 1);
check_op_decoding(&trace, 4, INIT_ADDR, Operation::Noop, 0, 0, 1);
check_op_decoding(&trace, 5, INIT_ADDR, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 6, ZERO, Operation::Halt, 0, 0, 0);
let program_hash: Word = program.hash().into();
check_hasher_state(
&trace,
vec![
span.op_batches()[0].groups().to_vec(),
vec![build_op_group(&ops[1..])],
vec![build_op_group(&ops[2..])],
vec![],
vec![],
program_hash.to_vec(), ],
);
for i in 7..trace_len {
assert!(contains_op(&trace, i, Operation::Halt));
assert_eq!(ZERO, trace[OP_BITS_EXTRA_COLS_RANGE.start][i]);
assert_eq!(ONE, trace[OP_BITS_EXTRA_COLS_RANGE.start + 1][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
}
#[test]
fn span_block() {
let iv = [ONE, TWO, Felt::new(3), Felt::new(4), Felt::new(5)];
let ops = vec![
Operation::Push(iv[0]),
Operation::Push(iv[1]),
Operation::Push(iv[2]),
Operation::Pad,
Operation::Mul,
Operation::Add,
Operation::Drop,
Operation::Push(iv[3]),
Operation::Push(iv[4]),
Operation::Mul,
Operation::Add,
Operation::Inv,
];
let span = Span::new(ops.clone());
let program = CodeBlock::new_span(ops.clone());
let (trace, trace_len) = build_trace(&[], &program);
check_op_decoding(&trace, 0, ZERO, Operation::Span, 8, 0, 0);
check_op_decoding(&trace, 1, INIT_ADDR, Operation::Push(iv[0]), 7, 0, 1);
check_op_decoding(&trace, 2, INIT_ADDR, Operation::Push(iv[1]), 6, 1, 1);
check_op_decoding(&trace, 3, INIT_ADDR, Operation::Push(iv[2]), 5, 2, 1);
check_op_decoding(&trace, 4, INIT_ADDR, Operation::Pad, 4, 3, 1);
check_op_decoding(&trace, 5, INIT_ADDR, Operation::Mul, 4, 4, 1);
check_op_decoding(&trace, 6, INIT_ADDR, Operation::Add, 4, 5, 1);
check_op_decoding(&trace, 7, INIT_ADDR, Operation::Drop, 4, 6, 1);
check_op_decoding(&trace, 8, INIT_ADDR, Operation::Push(iv[3]), 4, 7, 1);
check_op_decoding(&trace, 9, INIT_ADDR, Operation::Noop, 3, 8, 1);
check_op_decoding(&trace, 10, INIT_ADDR, Operation::Push(iv[4]), 2, 0, 1);
check_op_decoding(&trace, 11, INIT_ADDR, Operation::Mul, 1, 1, 1);
check_op_decoding(&trace, 12, INIT_ADDR, Operation::Add, 1, 2, 1);
check_op_decoding(&trace, 13, INIT_ADDR, Operation::Inv, 1, 3, 1);
check_op_decoding(&trace, 14, INIT_ADDR, Operation::Noop, 0, 0, 1);
check_op_decoding(&trace, 15, INIT_ADDR, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 16, ZERO, Operation::Halt, 0, 0, 0);
let program_hash: Word = program.hash().into();
check_hasher_state(
&trace,
vec![
span.op_batches()[0].groups().to_vec(),
vec![build_op_group(&ops[1..8])], vec![build_op_group(&ops[2..8])],
vec![build_op_group(&ops[3..8])],
vec![build_op_group(&ops[4..8])],
vec![build_op_group(&ops[5..8])],
vec![build_op_group(&ops[6..8])],
vec![build_op_group(&ops[7..8])],
vec![], vec![],
vec![build_op_group(&ops[9..])], vec![build_op_group(&ops[10..])],
vec![build_op_group(&ops[11..])],
vec![],
vec![], program_hash.to_vec(), ],
);
for i in 17..trace_len {
assert!(contains_op(&trace, i, Operation::Halt));
assert_eq!(ZERO, trace[OP_BITS_EXTRA_COLS_RANGE.start][i]);
assert_eq!(ONE, trace[OP_BITS_EXTRA_COLS_RANGE.start + 1][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
}
#[test]
fn span_block_with_respan() {
let iv = [
ONE,
TWO,
Felt::new(3),
Felt::new(4),
Felt::new(5),
Felt::new(6),
Felt::new(7),
EIGHT,
Felt::new(9),
];
let ops = vec![
Operation::Push(iv[0]),
Operation::Push(iv[1]),
Operation::Push(iv[2]),
Operation::Push(iv[3]),
Operation::Push(iv[4]),
Operation::Push(iv[5]),
Operation::Push(iv[6]),
Operation::Push(iv[7]),
Operation::Add,
Operation::Push(iv[8]),
];
let span = Span::new(ops.clone());
let program = CodeBlock::new_span(ops.clone());
let (trace, trace_len) = build_trace(&[], &program);
check_op_decoding(&trace, 0, ZERO, Operation::Span, 12, 0, 0);
check_op_decoding(&trace, 1, INIT_ADDR, Operation::Push(iv[0]), 11, 0, 1);
check_op_decoding(&trace, 2, INIT_ADDR, Operation::Push(iv[1]), 10, 1, 1);
check_op_decoding(&trace, 3, INIT_ADDR, Operation::Push(iv[2]), 9, 2, 1);
check_op_decoding(&trace, 4, INIT_ADDR, Operation::Push(iv[3]), 8, 3, 1);
check_op_decoding(&trace, 5, INIT_ADDR, Operation::Push(iv[4]), 7, 4, 1);
check_op_decoding(&trace, 6, INIT_ADDR, Operation::Push(iv[5]), 6, 5, 1);
check_op_decoding(&trace, 7, INIT_ADDR, Operation::Push(iv[6]), 5, 6, 1);
check_op_decoding(&trace, 8, INIT_ADDR, Operation::Noop, 4, 7, 1);
let batch1_addr = INIT_ADDR + EIGHT;
check_op_decoding(&trace, 9, INIT_ADDR, Operation::Respan, 4, 0, 0);
check_op_decoding(&trace, 10, batch1_addr, Operation::Push(iv[7]), 3, 0, 1);
check_op_decoding(&trace, 11, batch1_addr, Operation::Add, 2, 1, 1);
check_op_decoding(&trace, 12, batch1_addr, Operation::Push(iv[8]), 2, 2, 1);
check_op_decoding(&trace, 13, batch1_addr, Operation::Noop, 1, 3, 1);
check_op_decoding(&trace, 14, batch1_addr, Operation::Noop, 0, 0, 1);
check_op_decoding(&trace, 15, batch1_addr, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 16, ZERO, Operation::Halt, 0, 0, 0);
let program_hash: Word = program.hash().into();
check_hasher_state(
&trace,
vec![
span.op_batches()[0].groups().to_vec(),
vec![build_op_group(&ops[1..7])], vec![build_op_group(&ops[2..7])],
vec![build_op_group(&ops[3..7])],
vec![build_op_group(&ops[4..7])],
vec![build_op_group(&ops[5..7])],
vec![build_op_group(&ops[6..7])],
vec![],
vec![], span.op_batches()[1].groups().to_vec(),
vec![build_op_group(&ops[8..])], vec![build_op_group(&ops[9..])],
vec![],
vec![], vec![], program_hash.to_vec(), ],
);
for i in 17..trace_len {
assert!(contains_op(&trace, i, Operation::Halt));
assert_eq!(ZERO, trace[OP_BITS_EXTRA_COLS_RANGE.start][i]);
assert_eq!(ONE, trace[OP_BITS_EXTRA_COLS_RANGE.start + 1][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
}
#[test]
fn join_block() {
let span1 = CodeBlock::new_span(vec![Operation::Mul]);
let span2 = CodeBlock::new_span(vec![Operation::Add]);
let program = CodeBlock::new_join([span1.clone(), span2.clone()]);
let (trace, trace_len) = build_trace(&[], &program);
check_op_decoding(&trace, 0, ZERO, Operation::Join, 0, 0, 0);
let span1_addr = INIT_ADDR + EIGHT;
check_op_decoding(&trace, 1, INIT_ADDR, Operation::Span, 1, 0, 0);
check_op_decoding(&trace, 2, span1_addr, Operation::Mul, 0, 0, 1);
check_op_decoding(&trace, 3, span1_addr, Operation::End, 0, 0, 0);
let span2_addr = INIT_ADDR + Felt::new(16);
check_op_decoding(&trace, 4, INIT_ADDR, Operation::Span, 1, 0, 0);
check_op_decoding(&trace, 5, span2_addr, Operation::Add, 0, 0, 1);
check_op_decoding(&trace, 6, span2_addr, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 7, INIT_ADDR, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 8, ZERO, Operation::Halt, 0, 0, 0);
let span1_hash: Word = span1.hash().into();
let span2_hash: Word = span2.hash().into();
assert_eq!(span1_hash, get_hasher_state1(&trace, 0));
assert_eq!(span2_hash, get_hasher_state2(&trace, 0));
assert_eq!(span1_hash, get_hasher_state1(&trace, 3));
assert_eq!(EMPTY_WORD, get_hasher_state2(&trace, 3));
assert_eq!(span2_hash, get_hasher_state1(&trace, 6));
assert_eq!(EMPTY_WORD, get_hasher_state2(&trace, 6));
let program_hash: Word = program.hash().into();
assert_eq!(program_hash, get_hasher_state1(&trace, 7));
assert_eq!(EMPTY_WORD, get_hasher_state2(&trace, 7));
for i in 9..trace_len {
assert!(contains_op(&trace, i, Operation::Halt));
assert_eq!(ZERO, trace[OP_BITS_EXTRA_COLS_RANGE.start][i]);
assert_eq!(ONE, trace[OP_BITS_EXTRA_COLS_RANGE.start + 1][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
}
#[test]
fn split_block_true() {
let span1 = CodeBlock::new_span(vec![Operation::Mul]);
let span2 = CodeBlock::new_span(vec![Operation::Add]);
let program = CodeBlock::new_split(span1.clone(), span2.clone());
let (trace, trace_len) = build_trace(&[1], &program);
let span_addr = INIT_ADDR + EIGHT;
check_op_decoding(&trace, 0, ZERO, Operation::Split, 0, 0, 0);
check_op_decoding(&trace, 1, INIT_ADDR, Operation::Span, 1, 0, 0);
check_op_decoding(&trace, 2, span_addr, Operation::Mul, 0, 0, 1);
check_op_decoding(&trace, 3, span_addr, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 4, INIT_ADDR, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 5, ZERO, Operation::Halt, 0, 0, 0);
let span1_hash: Word = span1.hash().into();
let span2_hash: Word = span2.hash().into();
assert_eq!(span1_hash, get_hasher_state1(&trace, 0));
assert_eq!(span2_hash, get_hasher_state2(&trace, 0));
assert_eq!(span1_hash, get_hasher_state1(&trace, 3));
assert_eq!(EMPTY_WORD, get_hasher_state2(&trace, 3));
let program_hash: Word = program.hash().into();
assert_eq!(program_hash, get_hasher_state1(&trace, 4));
assert_eq!(EMPTY_WORD, get_hasher_state2(&trace, 4));
for i in 6..trace_len {
assert!(contains_op(&trace, i, Operation::Halt));
assert_eq!(ZERO, trace[OP_BITS_EXTRA_COLS_RANGE.start][i]);
assert_eq!(ONE, trace[OP_BITS_EXTRA_COLS_RANGE.start + 1][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
}
#[test]
fn split_block_false() {
let span1 = CodeBlock::new_span(vec![Operation::Mul]);
let span2 = CodeBlock::new_span(vec![Operation::Add]);
let program = CodeBlock::new_split(span1.clone(), span2.clone());
let (trace, trace_len) = build_trace(&[0], &program);
let span_addr = INIT_ADDR + EIGHT;
check_op_decoding(&trace, 0, ZERO, Operation::Split, 0, 0, 0);
check_op_decoding(&trace, 1, INIT_ADDR, Operation::Span, 1, 0, 0);
check_op_decoding(&trace, 2, span_addr, Operation::Add, 0, 0, 1);
check_op_decoding(&trace, 3, span_addr, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 4, INIT_ADDR, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 5, ZERO, Operation::Halt, 0, 0, 0);
let span1_hash: Word = span1.hash().into();
let span2_hash: Word = span2.hash().into();
assert_eq!(span1_hash, get_hasher_state1(&trace, 0));
assert_eq!(span2_hash, get_hasher_state2(&trace, 0));
assert_eq!(span2_hash, get_hasher_state1(&trace, 3));
assert_eq!(EMPTY_WORD, get_hasher_state2(&trace, 3));
let program_hash: Word = program.hash().into();
assert_eq!(program_hash, get_hasher_state1(&trace, 4));
assert_eq!(EMPTY_WORD, get_hasher_state2(&trace, 4));
for i in 6..trace_len {
assert!(contains_op(&trace, i, Operation::Halt));
assert_eq!(ZERO, trace[OP_BITS_EXTRA_COLS_RANGE.start][i]);
assert_eq!(ONE, trace[OP_BITS_EXTRA_COLS_RANGE.start + 1][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
}
#[test]
fn loop_block() {
let loop_body = CodeBlock::new_span(vec![Operation::Pad, Operation::Drop]);
let program = CodeBlock::new_loop(loop_body.clone());
let (trace, trace_len) = build_trace(&[0, 1], &program);
let body_addr = INIT_ADDR + EIGHT;
check_op_decoding(&trace, 0, ZERO, Operation::Loop, 0, 0, 0);
check_op_decoding(&trace, 1, INIT_ADDR, Operation::Span, 1, 0, 0);
check_op_decoding(&trace, 2, body_addr, Operation::Pad, 0, 0, 1);
check_op_decoding(&trace, 3, body_addr, Operation::Drop, 0, 1, 1);
check_op_decoding(&trace, 4, body_addr, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 5, INIT_ADDR, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 6, ZERO, Operation::Halt, 0, 0, 0);
let loop_body_hash: Word = loop_body.hash().into();
assert_eq!(loop_body_hash, get_hasher_state1(&trace, 0));
assert_eq!(EMPTY_WORD, get_hasher_state2(&trace, 0));
assert_eq!(loop_body_hash, get_hasher_state1(&trace, 4));
assert_eq!([ONE, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 4));
let program_hash: Word = program.hash().into();
assert_eq!(program_hash, get_hasher_state1(&trace, 5));
assert_eq!([ZERO, ONE, ZERO, ZERO], get_hasher_state2(&trace, 5));
for i in 7..trace_len {
assert!(contains_op(&trace, i, Operation::Halt));
assert_eq!(ZERO, trace[OP_BITS_EXTRA_COLS_RANGE.start][i]);
assert_eq!(ONE, trace[OP_BITS_EXTRA_COLS_RANGE.start + 1][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
}
#[test]
fn loop_block_skip() {
let loop_body = CodeBlock::new_span(vec![Operation::Pad, Operation::Drop]);
let program = CodeBlock::new_loop(loop_body.clone());
let (trace, trace_len) = build_trace(&[0], &program);
check_op_decoding(&trace, 0, ZERO, Operation::Loop, 0, 0, 0);
check_op_decoding(&trace, 1, INIT_ADDR, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 2, ZERO, Operation::Halt, 0, 0, 0);
let loop_body_hash: Word = loop_body.hash().into();
assert_eq!(loop_body_hash, get_hasher_state1(&trace, 0));
assert_eq!(EMPTY_WORD, get_hasher_state2(&trace, 0));
let program_hash: Word = program.hash().into();
assert_eq!(program_hash, get_hasher_state1(&trace, 1));
assert_eq!(EMPTY_WORD, get_hasher_state2(&trace, 1));
for i in 3..trace_len {
assert!(contains_op(&trace, i, Operation::Halt));
assert_eq!(ZERO, trace[OP_BITS_EXTRA_COLS_RANGE.start][i]);
assert_eq!(ONE, trace[OP_BITS_EXTRA_COLS_RANGE.start + 1][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
}
#[test]
fn loop_block_repeat() {
let loop_body = CodeBlock::new_span(vec![Operation::Pad, Operation::Drop]);
let program = CodeBlock::new_loop(loop_body.clone());
let (trace, trace_len) = build_trace(&[0, 1, 1], &program);
let iter1_addr = INIT_ADDR + EIGHT;
let iter2_addr = INIT_ADDR + Felt::new(16);
check_op_decoding(&trace, 0, ZERO, Operation::Loop, 0, 0, 0);
check_op_decoding(&trace, 1, INIT_ADDR, Operation::Span, 1, 0, 0);
check_op_decoding(&trace, 2, iter1_addr, Operation::Pad, 0, 0, 1);
check_op_decoding(&trace, 3, iter1_addr, Operation::Drop, 0, 1, 1);
check_op_decoding(&trace, 4, iter1_addr, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 5, INIT_ADDR, Operation::Repeat, 0, 0, 0);
check_op_decoding(&trace, 6, INIT_ADDR, Operation::Span, 1, 0, 0);
check_op_decoding(&trace, 7, iter2_addr, Operation::Pad, 0, 0, 1);
check_op_decoding(&trace, 8, iter2_addr, Operation::Drop, 0, 1, 1);
check_op_decoding(&trace, 9, iter2_addr, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 10, INIT_ADDR, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 11, ZERO, Operation::Halt, 0, 0, 0);
let loop_body_hash: Word = loop_body.hash().into();
assert_eq!(loop_body_hash, get_hasher_state1(&trace, 0));
assert_eq!(EMPTY_WORD, get_hasher_state2(&trace, 0));
assert_eq!(loop_body_hash, get_hasher_state1(&trace, 4));
assert_eq!([ONE, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 4));
assert_eq!(loop_body_hash, get_hasher_state1(&trace, 5));
assert_eq!([ONE, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 5));
assert_eq!(loop_body_hash, get_hasher_state1(&trace, 9));
assert_eq!([ONE, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 9));
let program_hash: Word = program.hash().into();
assert_eq!(program_hash, get_hasher_state1(&trace, 10));
assert_eq!([ZERO, ONE, ZERO, ZERO], get_hasher_state2(&trace, 10));
for i in 12..trace_len {
assert!(contains_op(&trace, i, Operation::Halt));
assert_eq!(ZERO, trace[OP_BITS_EXTRA_COLS_RANGE.start][i]);
assert_eq!(ONE, trace[OP_BITS_EXTRA_COLS_RANGE.start + 1][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
}
#[test]
#[rustfmt::skip]
fn call_block() {
let first_span = CodeBlock::new_span(vec![
Operation::Push(TWO),
Operation::FmpUpdate,
Operation::Pad,
]);
let foo_root = CodeBlock::new_span(vec![Operation::Push(ONE), Operation::FmpUpdate]);
let last_span = CodeBlock::new_span(vec![Operation::FmpAdd]);
let foo_call = CodeBlock::new_call(foo_root.hash());
let join1 = CodeBlock::new_join([first_span.clone(), foo_call.clone()]);
let program = CodeBlock::new_join([join1.clone(), last_span.clone()]);
let (sys_trace, dec_trace, trace_len) =
build_call_trace(&program, foo_root.clone(), None);
check_op_decoding(&dec_trace, 0, ZERO, Operation::Join, 0, 0, 0);
let join1_addr = INIT_ADDR + EIGHT;
check_op_decoding(&dec_trace, 1, INIT_ADDR, Operation::Join, 0, 0, 0);
let first_span_addr = join1_addr + EIGHT;
check_op_decoding(&dec_trace, 2, join1_addr, Operation::Span, 2, 0, 0);
check_op_decoding(&dec_trace, 3, first_span_addr, Operation::Push(TWO), 1, 0, 1);
check_op_decoding(&dec_trace, 4, first_span_addr, Operation::FmpUpdate, 0, 1, 1);
check_op_decoding(&dec_trace, 5, first_span_addr, Operation::Pad, 0, 2, 1);
check_op_decoding(&dec_trace, 6, first_span_addr, Operation::End, 0, 0, 0);
let foo_call_addr = first_span_addr + EIGHT;
check_op_decoding(&dec_trace, 7, join1_addr, Operation::Call, 0, 0, 0);
let foo_root_addr = foo_call_addr + EIGHT;
check_op_decoding(&dec_trace, 8, foo_call_addr, Operation::Span, 2, 0, 0);
check_op_decoding(&dec_trace, 9, foo_root_addr, Operation::Push(ONE), 1, 0, 1);
check_op_decoding(&dec_trace, 10, foo_root_addr, Operation::FmpUpdate, 0, 1, 1);
check_op_decoding(&dec_trace, 11, foo_root_addr, Operation::End, 0, 0, 0);
check_op_decoding(&dec_trace, 12, foo_call_addr, Operation::End, 0, 0, 0);
check_op_decoding(&dec_trace, 13, join1_addr, Operation::End, 0, 0, 0);
let last_span_addr = foo_root_addr + EIGHT;
check_op_decoding(&dec_trace, 14, INIT_ADDR, Operation::Span, 1, 0, 0);
check_op_decoding(&dec_trace, 15, last_span_addr, Operation::FmpAdd, 0, 0, 1);
check_op_decoding(&dec_trace, 16, last_span_addr, Operation::End, 0, 0, 0);
check_op_decoding(&dec_trace, 17, INIT_ADDR, Operation::End, 0, 0, 0);
check_op_decoding(&dec_trace, 18, ZERO, Operation::Halt, 0, 0, 0);
let join1_hash: Word = join1.hash().into();
let last_span_hash: Word = last_span.hash().into();
assert_eq!(join1_hash, get_hasher_state1(&dec_trace, 0));
assert_eq!(last_span_hash, get_hasher_state2(&dec_trace, 0));
let first_span_hash: Word = first_span.hash().into();
let foo_call_hash: Word = foo_call.hash().into();
assert_eq!(first_span_hash, get_hasher_state1(&dec_trace, 1));
assert_eq!(foo_call_hash, get_hasher_state2(&dec_trace, 1));
assert_eq!(first_span_hash, get_hasher_state1(&dec_trace, 6));
assert_eq!(EMPTY_WORD, get_hasher_state2(&dec_trace, 6));
let foo_root_hash: Word = foo_root.hash().into();
assert_eq!(foo_root_hash, get_hasher_state1(&dec_trace, 7));
assert_eq!(EMPTY_WORD, get_hasher_state2(&dec_trace, 7));
assert_eq!(foo_root_hash, get_hasher_state1(&dec_trace, 11));
assert_eq!(EMPTY_WORD, get_hasher_state2(&dec_trace, 11));
assert_eq!(foo_call_hash, get_hasher_state1(&dec_trace, 12));
assert_eq!([ZERO, ZERO, ONE, ZERO], get_hasher_state2(&dec_trace, 12));
assert_eq!(join1_hash, get_hasher_state1(&dec_trace, 13));
assert_eq!(EMPTY_WORD, get_hasher_state2(&dec_trace, 13));
assert_eq!(last_span_hash, get_hasher_state1(&dec_trace, 16));
assert_eq!(EMPTY_WORD, get_hasher_state2(&dec_trace, 16));
let program_hash: Word = program.hash().into();
assert_eq!(program_hash, get_hasher_state1(&dec_trace, 17));
assert_eq!(EMPTY_WORD, get_hasher_state2(&dec_trace, 17));
for i in 18..trace_len {
assert!(contains_op(&dec_trace, i, Operation::Halt));
assert_eq!(ZERO, dec_trace[OP_BITS_EXTRA_COLS_RANGE.start][i]);
assert_eq!(ONE, dec_trace[OP_BITS_EXTRA_COLS_RANGE.start + 1][i]);
assert_eq!(program_hash, get_hasher_state1(&dec_trace, i));
}
for i in 0..8 {
assert_eq!(sys_trace[CTX_COL_IDX][i], ZERO);
}
for i in 8..13 {
assert_eq!(sys_trace[CTX_COL_IDX][i], EIGHT);
}
for i in 13..trace_len {
assert_eq!(sys_trace[CTX_COL_IDX][i], ZERO);
}
for i in 0..5 {
assert_eq!(sys_trace[FMP_COL_IDX][i], FMP_MIN);
}
for i in 5..8 {
assert_eq!(sys_trace[FMP_COL_IDX][i], FMP_MIN + TWO);
}
for i in 8..11 {
assert_eq!(sys_trace[FMP_COL_IDX][i], FMP_MIN);
}
for i in 11..13 {
assert_eq!(sys_trace[FMP_COL_IDX][i], FMP_MIN + ONE);
}
for i in 13..trace_len {
assert_eq!(sys_trace[FMP_COL_IDX][i], FMP_MIN + TWO);
}
assert_eq!(
&sys_trace[IN_SYSCALL_COL_IDX][..trace_len],
vec![ZERO; trace_len]
);
for i in 0..8 {
assert_eq!(get_fn_hash(&sys_trace, i), EMPTY_WORD);
}
for i in 8..13 {
assert_eq!(get_fn_hash(&sys_trace, i), foo_root_hash);
}
for i in 13..trace_len {
assert_eq!(get_fn_hash(&sys_trace, i), EMPTY_WORD);
}
}
#[test]
#[rustfmt::skip]
fn syscall_block() {
let foo_root = CodeBlock::new_span(vec![Operation::Push(THREE), Operation::FmpUpdate]);
let bar_span = CodeBlock::new_span(vec![Operation::Push(TWO), Operation::FmpUpdate]);
let foo_call = CodeBlock::new_syscall(foo_root.hash());
let bar_root = CodeBlock::new_join([bar_span.clone(), foo_call.clone()]);
let first_span = CodeBlock::new_span(vec![
Operation::Push(ONE),
Operation::FmpUpdate,
Operation::Pad,
]);
let last_span = CodeBlock::new_span(vec![Operation::FmpAdd]);
let bar_call = CodeBlock::new_call(bar_root.hash());
let inner_join = CodeBlock::new_join([first_span.clone(), bar_call.clone()]);
let program = CodeBlock::new_join([inner_join.clone(), last_span.clone()]);
let (sys_trace, dec_trace, trace_len) =
build_call_trace(&program, bar_root.clone(), Some(foo_root.clone()));
check_op_decoding(&dec_trace, 0, ZERO, Operation::Join, 0, 0, 0);
let inner_join_addr = INIT_ADDR + EIGHT;
check_op_decoding(&dec_trace, 1, INIT_ADDR, Operation::Join, 0, 0, 0);
let first_span_addr = inner_join_addr + EIGHT;
check_op_decoding(&dec_trace, 2, inner_join_addr, Operation::Span, 2, 0, 0);
check_op_decoding(&dec_trace, 3, first_span_addr, Operation::Push(TWO), 1, 0, 1);
check_op_decoding(&dec_trace, 4, first_span_addr, Operation::FmpUpdate, 0, 1, 1);
check_op_decoding(&dec_trace, 5, first_span_addr, Operation::Pad, 0, 2, 1);
check_op_decoding(&dec_trace, 6, first_span_addr, Operation::End, 0, 0, 0);
let call_addr = first_span_addr + EIGHT;
check_op_decoding(&dec_trace, 7, inner_join_addr, Operation::Call, 0, 0, 0);
let bar_join_addr = call_addr + EIGHT;
check_op_decoding(&dec_trace, 8, call_addr, Operation::Join, 0, 0, 0);
let bar_span_addr = bar_join_addr + EIGHT;
check_op_decoding(&dec_trace, 9, bar_join_addr, Operation::Span, 2, 0, 0);
check_op_decoding(&dec_trace, 10, bar_span_addr, Operation::Push(ONE), 1, 0, 1);
check_op_decoding(&dec_trace, 11, bar_span_addr, Operation::FmpUpdate, 0, 1, 1);
check_op_decoding(&dec_trace, 12, bar_span_addr, Operation::End, 0, 0, 0);
let syscall_addr = bar_span_addr + EIGHT;
check_op_decoding(&dec_trace, 13, bar_join_addr, Operation::SysCall, 0, 0, 0);
let syscall_span_addr = syscall_addr + EIGHT;
check_op_decoding(&dec_trace, 14, syscall_addr, Operation::Span, 2, 0, 0);
check_op_decoding(&dec_trace, 15, syscall_span_addr, Operation::Push(THREE), 1, 0, 1);
check_op_decoding(&dec_trace, 16, syscall_span_addr, Operation::FmpUpdate, 0, 1, 1);
check_op_decoding(&dec_trace, 17, syscall_span_addr, Operation::End, 0, 0, 0);
check_op_decoding(&dec_trace, 18, syscall_addr, Operation::End, 0, 0, 0);
check_op_decoding(&dec_trace, 19, bar_join_addr, Operation::End, 0, 0, 0);
check_op_decoding(&dec_trace, 20, call_addr, Operation::End, 0, 0, 0);
check_op_decoding(&dec_trace, 21, inner_join_addr, Operation::End, 0, 0, 0);
let last_span_addr = syscall_span_addr + EIGHT;
check_op_decoding(&dec_trace, 22, INIT_ADDR, Operation::Span, 1, 0, 0);
check_op_decoding(&dec_trace, 23, last_span_addr, Operation::FmpAdd, 0, 0, 1);
check_op_decoding(&dec_trace, 24, last_span_addr, Operation::End, 0, 0, 0);
check_op_decoding(&dec_trace, 25, INIT_ADDR, Operation::End, 0, 0, 0);
check_op_decoding(&dec_trace, 26, ZERO, Operation::Halt, 0, 0, 0);
let inner_join_hash: Word = inner_join.hash().into();
let last_span_hash: Word = last_span.hash().into();
assert_eq!(inner_join_hash, get_hasher_state1(&dec_trace, 0));
assert_eq!(last_span_hash, get_hasher_state2(&dec_trace, 0));
let first_span_hash: Word = first_span.hash().into();
let bar_call_hash: Word = bar_call.hash().into();
assert_eq!(first_span_hash, get_hasher_state1(&dec_trace, 1));
assert_eq!(bar_call_hash, get_hasher_state2(&dec_trace, 1));
assert_eq!(first_span_hash, get_hasher_state1(&dec_trace, 6));
assert_eq!(EMPTY_WORD, get_hasher_state2(&dec_trace, 6));
let bar_root_hash: Word = bar_root.hash().into();
assert_eq!(bar_root_hash, get_hasher_state1(&dec_trace, 7));
assert_eq!(EMPTY_WORD, get_hasher_state2(&dec_trace, 7));
let bar_span_hash: Word = bar_span.hash().into();
let foo_call_hash: Word = foo_call.hash().into();
assert_eq!(bar_span_hash, get_hasher_state1(&dec_trace, 8));
assert_eq!(foo_call_hash, get_hasher_state2(&dec_trace, 8));
assert_eq!(bar_span_hash, get_hasher_state1(&dec_trace, 12));
assert_eq!(EMPTY_WORD, get_hasher_state2(&dec_trace, 12));
let foo_root_hash: Word = foo_root.hash().into();
assert_eq!(foo_root_hash, get_hasher_state1(&dec_trace, 13));
assert_eq!(EMPTY_WORD, get_hasher_state2(&dec_trace, 13));
assert_eq!(foo_root_hash, get_hasher_state1(&dec_trace, 17));
assert_eq!(EMPTY_WORD, get_hasher_state2(&dec_trace, 17));
assert_eq!(foo_call_hash, get_hasher_state1(&dec_trace, 18));
assert_eq!([ZERO, ZERO, ZERO, ONE], get_hasher_state2(&dec_trace, 18));
assert_eq!(bar_root_hash, get_hasher_state1(&dec_trace, 19));
assert_eq!(EMPTY_WORD, get_hasher_state2(&dec_trace, 19));
assert_eq!(bar_call_hash, get_hasher_state1(&dec_trace, 20));
assert_eq!([ZERO, ZERO, ONE, ZERO], get_hasher_state2(&dec_trace, 20));
assert_eq!(inner_join_hash, get_hasher_state1(&dec_trace, 21));
assert_eq!(EMPTY_WORD, get_hasher_state2(&dec_trace, 21));
assert_eq!(last_span_hash, get_hasher_state1(&dec_trace, 24));
assert_eq!(EMPTY_WORD, get_hasher_state2(&dec_trace, 24));
let program_hash: Word = program.hash().into();
assert_eq!(program_hash, get_hasher_state1(&dec_trace, 25));
assert_eq!(EMPTY_WORD, get_hasher_state2(&dec_trace, 25));
for i in 26..trace_len {
assert!(contains_op(&dec_trace, i, Operation::Halt));
assert_eq!(ZERO, dec_trace[OP_BITS_EXTRA_COLS_RANGE.start][i]);
assert_eq!(ONE, dec_trace[OP_BITS_EXTRA_COLS_RANGE.start + 1][i]);
assert_eq!(program_hash, get_hasher_state1(&dec_trace, i));
}
for i in 0..8 {
assert_eq!(sys_trace[CTX_COL_IDX][i], ZERO);
}
for i in 8..14 {
assert_eq!(sys_trace[CTX_COL_IDX][i], EIGHT);
}
for i in 14..18 {
assert_eq!(sys_trace[CTX_COL_IDX][i], ZERO);
}
for i in 19..21 {
assert_eq!(sys_trace[CTX_COL_IDX][i], EIGHT);
}
for i in 21..trace_len {
assert_eq!(sys_trace[CTX_COL_IDX][i], ZERO);
}
for i in 0..5 {
assert_eq!(sys_trace[FMP_COL_IDX][i], FMP_MIN);
}
for i in 5..8 {
assert_eq!(sys_trace[FMP_COL_IDX][i], FMP_MIN + ONE);
}
for i in 8..12 {
assert_eq!(sys_trace[FMP_COL_IDX][i], FMP_MIN);
}
for i in 12..14 {
assert_eq!(sys_trace[FMP_COL_IDX][i], FMP_MIN + TWO);
}
for i in 14..17 {
assert_eq!(sys_trace[FMP_COL_IDX][i], SYSCALL_FMP_MIN);
}
for i in 17..19 {
assert_eq!(sys_trace[FMP_COL_IDX][i], SYSCALL_FMP_MIN + THREE);
}
for i in 19..21 {
assert_eq!(sys_trace[FMP_COL_IDX][i], FMP_MIN + TWO);
}
for i in 21..trace_len {
assert_eq!(sys_trace[FMP_COL_IDX][i], FMP_MIN + ONE);
}
for i in 0..14 {
assert_eq!(sys_trace[IN_SYSCALL_COL_IDX][i], ZERO);
}
for i in 14..19 {
assert_eq!(sys_trace[IN_SYSCALL_COL_IDX][i], ONE);
}
for i in 19..trace_len {
assert_eq!(sys_trace[IN_SYSCALL_COL_IDX][i], ZERO);
}
for i in 0..8 {
assert_eq!(get_fn_hash(&sys_trace, i), EMPTY_WORD);
}
for i in 8..21 {
assert_eq!(get_fn_hash(&sys_trace, i), bar_root_hash);
}
for i in 21..trace_len {
assert_eq!(get_fn_hash(&sys_trace, i), EMPTY_WORD);
}
}
#[test]
fn dyn_block() {
let foo_root = CodeBlock::new_span(vec![Operation::Push(ONE), Operation::Add]);
let mul_span = CodeBlock::new_span(vec![Operation::Mul]);
let save_span = CodeBlock::new_span(vec![Operation::MovDn4]);
let join = CodeBlock::new_join([mul_span.clone(), save_span.clone()]);
let dyn_block = CodeBlock::new_dyn();
let program = CodeBlock::new_join([join.clone(), dyn_block.clone()]);
let (trace, trace_len) = build_dyn_trace(
&[
foo_root.hash()[0].as_int(),
foo_root.hash()[1].as_int(),
foo_root.hash()[2].as_int(),
foo_root.hash()[3].as_int(),
2,
4,
],
&program,
foo_root.clone(),
);
check_op_decoding(&trace, 0, ZERO, Operation::Join, 0, 0, 0);
let join_addr = INIT_ADDR + EIGHT;
check_op_decoding(&trace, 1, INIT_ADDR, Operation::Join, 0, 0, 0);
let mul_span_addr = join_addr + EIGHT;
check_op_decoding(&trace, 2, join_addr, Operation::Span, 1, 0, 0);
check_op_decoding(&trace, 3, mul_span_addr, Operation::Mul, 0, 0, 1);
check_op_decoding(&trace, 4, mul_span_addr, Operation::End, 0, 0, 0);
let save_span_addr = mul_span_addr + EIGHT;
check_op_decoding(&trace, 5, join_addr, Operation::Span, 1, 0, 0);
check_op_decoding(&trace, 6, save_span_addr, Operation::MovDn4, 0, 0, 1);
check_op_decoding(&trace, 7, save_span_addr, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 8, join_addr, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 9, INIT_ADDR, Operation::Dyn, 0, 0, 0);
let dyn_addr = save_span_addr + EIGHT;
let add_span_addr = dyn_addr + EIGHT;
check_op_decoding(&trace, 10, dyn_addr, Operation::Span, 2, 0, 0);
check_op_decoding(&trace, 11, add_span_addr, Operation::Push(ONE), 1, 0, 1);
check_op_decoding(&trace, 12, add_span_addr, Operation::Add, 0, 1, 1);
check_op_decoding(&trace, 13, add_span_addr, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 14, dyn_addr, Operation::End, 0, 0, 0);
check_op_decoding(&trace, 15, INIT_ADDR, Operation::End, 0, 0, 0);
let join_hash: Word = join.hash().into();
let dyn_hash: Word = dyn_block.hash().into();
assert_eq!(join_hash, get_hasher_state1(&trace, 0));
assert_eq!(dyn_hash, get_hasher_state2(&trace, 0));
let mul_span_hash: Word = mul_span.hash().into();
let save_span_hash: Word = save_span.hash().into();
assert_eq!(mul_span_hash, get_hasher_state1(&trace, 1));
assert_eq!(save_span_hash, get_hasher_state2(&trace, 1));
assert_eq!(mul_span_hash, get_hasher_state1(&trace, 4));
assert_eq!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 4));
assert_eq!(save_span_hash, get_hasher_state1(&trace, 7));
assert_eq!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 7));
assert_eq!(join_hash, get_hasher_state1(&trace, 8));
assert_eq!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 8));
let foo_hash: Word = foo_root.hash().into();
assert_eq!(foo_hash, get_hasher_state1(&trace, 9));
assert_eq!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 9));
assert_eq!(foo_hash, get_hasher_state1(&trace, 13));
assert_eq!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 13));
assert_eq!(dyn_hash, get_hasher_state1(&trace, 14));
let program_hash: Word = program.hash().into();
assert_eq!(program_hash, get_hasher_state1(&trace, 15));
assert_eq!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 15));
for i in 16..trace_len {
assert!(contains_op(&trace, i, Operation::Halt));
assert_eq!(ZERO, trace[OP_BITS_EXTRA_COLS_RANGE.start][i]);
assert_eq!(ONE, trace[OP_BITS_EXTRA_COLS_RANGE.start + 1][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
}
#[test]
fn set_user_op_helpers_many() {
let program = CodeBlock::new_span(vec![Operation::U32div]);
let a = rand_value::<u32>();
let b = rand_value::<u32>();
let (dividend, divisor) = if a > b { (a, b) } else { (b, a) };
let (trace, ..) = build_trace(&[dividend as u64, divisor as u64], &program);
let hasher_state = get_hasher_state(&trace, 1);
let quot = dividend / divisor;
let rem = dividend - quot * divisor;
let check_1 = dividend - quot;
let check_2 = divisor - rem - 1;
let expected = build_expected_hasher_state(&[
ZERO,
ZERO,
Felt::new((check_1 as u16).into()),
Felt::new(((check_1 >> 16) as u16).into()),
Felt::new((check_2 as u16).into()),
Felt::new(((check_2 >> 16) as u16).into()),
]);
assert_eq!(expected, hasher_state);
}
fn build_trace(stack_inputs: &[u64], program: &CodeBlock) -> (DecoderTrace, usize) {
let stack_inputs = StackInputs::try_from_ints(stack_inputs.iter().copied()).unwrap();
let host = DefaultHost::default();
let mut process =
Process::new(Kernel::default(), stack_inputs, host, ExecutionOptions::default());
process.execute_code_block(program, &CodeBlockTable::default()).unwrap();
let (trace, _, _) = ExecutionTrace::test_finalize_trace(process);
let trace_len = trace.num_rows() - ExecutionTrace::NUM_RAND_ROWS;
(
trace
.get_column_range(DECODER_TRACE_RANGE)
.try_into()
.expect("failed to convert vector to array"),
trace_len,
)
}
fn build_dyn_trace(
stack_inputs: &[u64],
program: &CodeBlock,
fn_block: CodeBlock,
) -> (DecoderTrace, usize) {
let stack_inputs = StackInputs::try_from_ints(stack_inputs.iter().copied()).unwrap();
let host = DefaultHost::default();
let mut process =
Process::new(Kernel::default(), stack_inputs, host, ExecutionOptions::default());
let mut cb_table = CodeBlockTable::default();
cb_table.insert(fn_block);
process.execute_code_block(program, &cb_table).unwrap();
let (trace, _, _) = ExecutionTrace::test_finalize_trace(process);
let trace_len = trace.num_rows() - ExecutionTrace::NUM_RAND_ROWS;
(
trace
.get_column_range(DECODER_TRACE_RANGE)
.try_into()
.expect("failed to convert vector to array"),
trace_len,
)
}
fn build_call_trace(
program: &CodeBlock,
fn_block: CodeBlock,
kernel_proc: Option<CodeBlock>,
) -> (SystemTrace, DecoderTrace, usize) {
let kernel = match kernel_proc {
Some(ref proc) => Kernel::new(&[proc.hash()]).unwrap(),
None => Kernel::default(),
};
let host = DefaultHost::default();
let stack_inputs = crate::StackInputs::default();
let mut process = Process::new(kernel, stack_inputs, host, ExecutionOptions::default());
let mut cb_table = CodeBlockTable::default();
cb_table.insert(fn_block);
if let Some(proc) = kernel_proc {
cb_table.insert(proc);
}
process.execute_code_block(program, &cb_table).unwrap();
let (trace, _, _) = ExecutionTrace::test_finalize_trace(process);
let trace_len = trace.num_rows() - ExecutionTrace::NUM_RAND_ROWS;
let sys_trace = trace
.get_column_range(SYS_TRACE_RANGE)
.try_into()
.expect("failed to convert vector to array");
let decoder_trace = trace
.get_column_range(DECODER_TRACE_RANGE)
.try_into()
.expect("failed to convert vector to array");
(sys_trace, decoder_trace, trace_len)
}
fn check_op_decoding(
trace: &DecoderTrace,
row_idx: usize,
addr: Felt,
op: Operation,
group_count: u64,
op_idx: u64,
in_span: u64,
) {
let opcode = read_opcode(trace, row_idx);
assert_eq!(trace[ADDR_COL_IDX][row_idx], addr);
assert_eq!(op.op_code(), opcode);
assert_eq!(trace[IN_SPAN_COL_IDX][row_idx], Felt::new(in_span));
assert_eq!(trace[GROUP_COUNT_COL_IDX][row_idx], Felt::new(group_count));
assert_eq!(trace[OP_INDEX_COL_IDX][row_idx], Felt::new(op_idx));
let expected_batch_flags = if op == Operation::Span || op == Operation::Respan {
let num_groups = core::cmp::min(OP_BATCH_SIZE, group_count as usize);
build_op_batch_flags(num_groups)
} else {
[ZERO, ZERO, ZERO]
};
for (i, flag_value) in OP_BATCH_FLAGS_RANGE.zip(expected_batch_flags) {
assert_eq!(trace[i][row_idx], flag_value);
}
let bit6 = Felt::from((opcode >> 6) & 1);
let bit5 = Felt::from((opcode >> 5) & 1);
let bit4 = Felt::from((opcode >> 4) & 1);
assert_eq!(trace[OP_BITS_EXTRA_COLS_RANGE.start][row_idx], bit6 * (ONE - bit5) * bit4);
assert_eq!(trace[OP_BITS_EXTRA_COLS_RANGE.start + 1][row_idx], bit6 * bit5);
}
fn contains_op(trace: &DecoderTrace, row_idx: usize, op: Operation) -> bool {
op.op_code() == read_opcode(trace, row_idx)
}
fn read_opcode(trace: &DecoderTrace, row_idx: usize) -> u8 {
let mut result = 0;
for (i, column) in trace.iter().skip(OP_BITS_OFFSET).take(NUM_OP_BITS).enumerate() {
let op_bit = column[row_idx].as_int();
assert!(op_bit <= 1, "invalid op bit");
result += op_bit << i;
}
result as u8
}
fn build_op_batch_flags(num_groups: usize) -> [Felt; NUM_OP_BATCH_FLAGS] {
match num_groups {
1 => OP_BATCH_1_GROUPS,
2 => OP_BATCH_2_GROUPS,
4 => OP_BATCH_4_GROUPS,
8 => OP_BATCH_8_GROUPS,
_ => panic!("invalid num groups: {num_groups}"),
}
}
fn get_fn_hash(trace: &SystemTrace, row_idx: usize) -> Word {
let mut result = EMPTY_WORD;
let trace = &trace[FN_HASH_RANGE];
for (element, column) in result.iter_mut().zip(trace) {
*element = column[row_idx];
}
result
}
fn check_hasher_state(trace: &DecoderTrace, expected: Vec<Vec<Felt>>) {
for (i, expected) in expected.iter().enumerate() {
let expected = build_expected_hasher_state(expected);
assert_eq!(expected, get_hasher_state(trace, i));
}
}
fn get_hasher_state(trace: &DecoderTrace, row_idx: usize) -> [Felt; NUM_HASHER_COLUMNS] {
let mut result = [ZERO; NUM_HASHER_COLUMNS];
for (result, column) in result.iter_mut().zip(trace[HASHER_STATE_RANGE].iter()) {
*result = column[row_idx];
}
result
}
fn get_hasher_state1(trace: &DecoderTrace, row_idx: usize) -> Word {
let mut result = EMPTY_WORD;
for (result, column) in result.iter_mut().zip(trace[HASHER_STATE_RANGE].iter()) {
*result = column[row_idx];
}
result
}
fn get_hasher_state2(trace: &DecoderTrace, row_idx: usize) -> Word {
let mut result = EMPTY_WORD;
for (result, column) in result.iter_mut().zip(trace[HASHER_STATE_RANGE].iter().skip(4)) {
*result = column[row_idx];
}
result
}
fn build_expected_hasher_state(values: &[Felt]) -> [Felt; NUM_HASHER_COLUMNS] {
let mut result = [ZERO; NUM_HASHER_COLUMNS];
for (i, value) in values.iter().enumerate() {
result[i] = *value;
}
result
}