use super::{
build_op_group, AuxTraceHints, BlockHashTableRow, BlockStackTableRow, BlockTableUpdate,
OpGroupTableRow, OpGroupTableUpdate,
};
use crate::{
decoder::block_stack::ExecutionContextInfo, utils::get_trace_len, ExecutionTrace, Felt, Kernel,
Operation, Process, ProgramInputs, Word,
};
use rand_utils::rand_value;
use vm_core::{
code_blocks::{CodeBlock, Span, OP_BATCH_SIZE},
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_OFFSET, OP_BITS_RANGE,
OP_BIT_EXTRA_COL_IDX, OP_INDEX_COL_IDX,
},
utils::collections::Vec,
CodeBlockTable, StarkField, CTX_COL_IDX, DECODER_TRACE_RANGE, DECODER_TRACE_WIDTH, FMP_COL_IDX,
FN_HASH_RANGE, IN_SYSCALL_COL_IDX, ONE, SYS_TRACE_RANGE, SYS_TRACE_WIDTH, 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);
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, aux_hints, 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!(ONE, trace[OP_BIT_EXTRA_COL_IDX][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
assert!(&aux_hints.op_group_table_hints().is_empty());
assert!(aux_hints.op_group_table_rows().is_empty());
let expected_hints = vec![
(0, BlockTableUpdate::BlockStarted(0)),
(4, BlockTableUpdate::BlockEnded(false)),
];
assert_eq!(expected_hints, aux_hints.block_exec_hints());
let expected_rows = vec![BlockStackTableRow::new_test(INIT_ADDR, ZERO, false)];
assert_eq!(expected_rows, aux_hints.block_stack_table_rows());
let expected_rows = vec![BlockHashTableRow::from_program_hash(program_hash)];
assert_eq!(expected_rows, aux_hints.block_hash_table_rows());
}
#[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, aux_hints, 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!(ONE, trace[OP_BIT_EXTRA_COL_IDX][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
let expected_ogt_hints = vec![
(0, OpGroupTableUpdate::InsertRows(3)),
(1, OpGroupTableUpdate::RemoveRow),
(2, OpGroupTableUpdate::RemoveRow),
(3, OpGroupTableUpdate::RemoveRow),
];
assert_eq!(&expected_ogt_hints, aux_hints.op_group_table_hints());
let expected_ogt_rows = vec![
OpGroupTableRow::new(INIT_ADDR, Felt::new(3), iv[0]),
OpGroupTableRow::new(INIT_ADDR, TWO, iv[1]),
OpGroupTableRow::new(INIT_ADDR, ONE, ZERO),
];
assert_eq!(expected_ogt_rows, aux_hints.op_group_table_rows());
let expected_hints = vec![
(0, BlockTableUpdate::BlockStarted(0)),
(5, BlockTableUpdate::BlockEnded(false)),
];
assert_eq!(expected_hints, aux_hints.block_exec_hints());
let expected_rows = vec![BlockStackTableRow::new_test(INIT_ADDR, ZERO, false)];
assert_eq!(expected_rows, aux_hints.block_stack_table_rows());
let expected_rows = vec![BlockHashTableRow::from_program_hash(program_hash)];
assert_eq!(expected_rows, aux_hints.block_hash_table_rows());
}
#[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, aux_hints, 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!(ONE, trace[OP_BIT_EXTRA_COL_IDX][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
let expected_ogt_hints = vec![
(0, OpGroupTableUpdate::InsertRows(7)),
(1, OpGroupTableUpdate::RemoveRow),
(2, OpGroupTableUpdate::RemoveRow),
(3, OpGroupTableUpdate::RemoveRow),
(8, OpGroupTableUpdate::RemoveRow),
(9, OpGroupTableUpdate::RemoveRow),
(10, OpGroupTableUpdate::RemoveRow),
(13, OpGroupTableUpdate::RemoveRow),
];
assert_eq!(&expected_ogt_hints, aux_hints.op_group_table_hints());
let batch0_groups = &span.op_batches()[0].groups();
let expected_ogt_rows = vec![
OpGroupTableRow::new(INIT_ADDR, Felt::new(7), batch0_groups[1]),
OpGroupTableRow::new(INIT_ADDR, Felt::new(6), batch0_groups[2]),
OpGroupTableRow::new(INIT_ADDR, Felt::new(5), batch0_groups[3]),
OpGroupTableRow::new(INIT_ADDR, Felt::new(4), batch0_groups[4]),
OpGroupTableRow::new(INIT_ADDR, Felt::new(3), batch0_groups[5]),
OpGroupTableRow::new(INIT_ADDR, TWO, batch0_groups[6]),
OpGroupTableRow::new(INIT_ADDR, ONE, batch0_groups[7]),
];
assert_eq!(expected_ogt_rows, aux_hints.op_group_table_rows());
let expected_hints = vec![
(0, BlockTableUpdate::BlockStarted(0)),
(15, BlockTableUpdate::BlockEnded(false)),
];
assert_eq!(expected_hints, aux_hints.block_exec_hints());
let expected_rows = vec![BlockStackTableRow::new_test(INIT_ADDR, ZERO, false)];
assert_eq!(expected_rows, aux_hints.block_stack_table_rows());
let expected_rows = vec![BlockHashTableRow::from_program_hash(program_hash)];
assert_eq!(expected_rows, aux_hints.block_hash_table_rows());
}
#[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, aux_hints, 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!(ONE, trace[OP_BIT_EXTRA_COL_IDX][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
let expected_ogt_hints = vec![
(0, OpGroupTableUpdate::InsertRows(7)),
(1, OpGroupTableUpdate::RemoveRow),
(2, OpGroupTableUpdate::RemoveRow),
(3, OpGroupTableUpdate::RemoveRow),
(4, OpGroupTableUpdate::RemoveRow),
(5, OpGroupTableUpdate::RemoveRow),
(6, OpGroupTableUpdate::RemoveRow),
(7, OpGroupTableUpdate::RemoveRow),
(9, OpGroupTableUpdate::InsertRows(3)),
(10, OpGroupTableUpdate::RemoveRow),
(12, OpGroupTableUpdate::RemoveRow),
(13, OpGroupTableUpdate::RemoveRow),
];
assert_eq!(&expected_ogt_hints, aux_hints.op_group_table_hints());
let batch0_groups = &span.op_batches()[0].groups();
let batch1_groups = &span.op_batches()[1].groups();
let expected_ogt_rows = vec![
OpGroupTableRow::new(INIT_ADDR, Felt::new(11), batch0_groups[1]),
OpGroupTableRow::new(INIT_ADDR, Felt::new(10), batch0_groups[2]),
OpGroupTableRow::new(INIT_ADDR, Felt::new(9), batch0_groups[3]),
OpGroupTableRow::new(INIT_ADDR, EIGHT, batch0_groups[4]),
OpGroupTableRow::new(INIT_ADDR, Felt::new(7), batch0_groups[5]),
OpGroupTableRow::new(INIT_ADDR, Felt::new(6), batch0_groups[6]),
OpGroupTableRow::new(INIT_ADDR, Felt::new(5), batch0_groups[7]),
OpGroupTableRow::new(batch1_addr, Felt::new(3), batch1_groups[1]),
OpGroupTableRow::new(batch1_addr, TWO, batch1_groups[2]),
OpGroupTableRow::new(batch1_addr, ONE, batch1_groups[3]),
];
assert_eq!(expected_ogt_rows, aux_hints.op_group_table_rows());
let expected_hints = vec![
(0, BlockTableUpdate::BlockStarted(0)),
(9, BlockTableUpdate::SpanExtended),
(15, BlockTableUpdate::BlockEnded(false)),
];
assert_eq!(expected_hints, aux_hints.block_exec_hints());
let expected_rows = vec![
BlockStackTableRow::new_test(INIT_ADDR, ZERO, false),
BlockStackTableRow::new_test(batch1_addr, ZERO, false),
];
assert_eq!(expected_rows, aux_hints.block_stack_table_rows());
let expected_rows = vec![BlockHashTableRow::from_program_hash(program_hash)];
assert_eq!(expected_rows, aux_hints.block_hash_table_rows());
}
#[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, aux_hints, 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!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 3));
assert_eq!(span2_hash, get_hasher_state1(&trace, 6));
assert_eq!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 6));
let program_hash: Word = program.hash().into();
assert_eq!(program_hash, get_hasher_state1(&trace, 7));
assert_eq!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 7));
for i in 9..trace_len {
assert!(contains_op(&trace, i, Operation::Halt));
assert_eq!(ONE, trace[OP_BIT_EXTRA_COL_IDX][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
assert!(&aux_hints.op_group_table_hints().is_empty());
assert!(aux_hints.op_group_table_rows().is_empty());
let expected_hints = vec![
(0, BlockTableUpdate::BlockStarted(2)),
(1, BlockTableUpdate::BlockStarted(0)),
(3, BlockTableUpdate::BlockEnded(true)),
(4, BlockTableUpdate::BlockStarted(0)),
(6, BlockTableUpdate::BlockEnded(false)),
(7, BlockTableUpdate::BlockEnded(false)),
];
assert_eq!(expected_hints, aux_hints.block_exec_hints());
let expected_rows = vec![
BlockStackTableRow::new_test(INIT_ADDR, ZERO, false),
BlockStackTableRow::new_test(span1_addr, INIT_ADDR, false),
BlockStackTableRow::new_test(span2_addr, INIT_ADDR, false),
];
assert_eq!(expected_rows, aux_hints.block_stack_table_rows());
let expected_rows = vec![
BlockHashTableRow::from_program_hash(program_hash),
BlockHashTableRow::new_test(INIT_ADDR, span1_hash, true, false),
BlockHashTableRow::new_test(INIT_ADDR, span2_hash, false, false),
];
assert_eq!(expected_rows, aux_hints.block_hash_table_rows());
}
#[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, aux_hints, 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!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 3));
let program_hash: Word = program.hash().into();
assert_eq!(program_hash, get_hasher_state1(&trace, 4));
assert_eq!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 4));
for i in 6..trace_len {
assert!(contains_op(&trace, i, Operation::Halt));
assert_eq!(ONE, trace[OP_BIT_EXTRA_COL_IDX][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
assert!(&aux_hints.op_group_table_hints().is_empty());
assert!(aux_hints.op_group_table_rows().is_empty());
let expected_hints = vec![
(0, BlockTableUpdate::BlockStarted(1)),
(1, BlockTableUpdate::BlockStarted(0)),
(3, BlockTableUpdate::BlockEnded(false)),
(4, BlockTableUpdate::BlockEnded(false)),
];
assert_eq!(expected_hints, aux_hints.block_exec_hints());
let expected_rows = vec![
BlockStackTableRow::new_test(INIT_ADDR, ZERO, false),
BlockStackTableRow::new_test(span_addr, INIT_ADDR, false),
];
assert_eq!(expected_rows, aux_hints.block_stack_table_rows());
let expected_rows = vec![
BlockHashTableRow::from_program_hash(program_hash),
BlockHashTableRow::new_test(INIT_ADDR, span1_hash, false, false),
];
assert_eq!(expected_rows, aux_hints.block_hash_table_rows());
}
#[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, aux_hints, 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!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 3));
let program_hash: Word = program.hash().into();
assert_eq!(program_hash, get_hasher_state1(&trace, 4));
assert_eq!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 4));
for i in 6..trace_len {
assert!(contains_op(&trace, i, Operation::Halt));
assert_eq!(ONE, trace[OP_BIT_EXTRA_COL_IDX][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
assert!(&aux_hints.op_group_table_hints().is_empty());
assert!(aux_hints.op_group_table_rows().is_empty());
let expected_hints = vec![
(0, BlockTableUpdate::BlockStarted(1)),
(1, BlockTableUpdate::BlockStarted(0)),
(3, BlockTableUpdate::BlockEnded(false)),
(4, BlockTableUpdate::BlockEnded(false)),
];
assert_eq!(expected_hints, aux_hints.block_exec_hints());
let expected_rows = vec![
BlockStackTableRow::new_test(INIT_ADDR, ZERO, false),
BlockStackTableRow::new_test(span_addr, INIT_ADDR, false),
];
assert_eq!(expected_rows, aux_hints.block_stack_table_rows());
let expected_rows = vec![
BlockHashTableRow::from_program_hash(program_hash),
BlockHashTableRow::new_test(INIT_ADDR, span2_hash, false, false),
];
assert_eq!(expected_rows, aux_hints.block_hash_table_rows());
}
#[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, aux_hints, 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!([ZERO; 4], 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!(ONE, trace[OP_BIT_EXTRA_COL_IDX][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
assert!(&aux_hints.op_group_table_hints().is_empty());
assert!(aux_hints.op_group_table_rows().is_empty());
let expected_hints = vec![
(0, BlockTableUpdate::BlockStarted(1)),
(1, BlockTableUpdate::BlockStarted(0)),
(4, BlockTableUpdate::BlockEnded(false)),
(5, BlockTableUpdate::BlockEnded(false)),
];
assert_eq!(expected_hints, aux_hints.block_exec_hints());
let expected_rows = vec![
BlockStackTableRow::new_test(INIT_ADDR, ZERO, true),
BlockStackTableRow::new_test(body_addr, INIT_ADDR, false),
];
assert_eq!(expected_rows, aux_hints.block_stack_table_rows());
let expected_rows = vec![
BlockHashTableRow::from_program_hash(program_hash),
BlockHashTableRow::new_test(INIT_ADDR, loop_body_hash, false, true),
];
assert_eq!(expected_rows, aux_hints.block_hash_table_rows());
}
#[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, aux_hints, 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!([ZERO; 4], get_hasher_state2(&trace, 0));
let program_hash: Word = program.hash().into();
assert_eq!(program_hash, get_hasher_state1(&trace, 1));
assert_eq!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&trace, 1));
for i in 3..trace_len {
assert!(contains_op(&trace, i, Operation::Halt));
assert_eq!(ONE, trace[OP_BIT_EXTRA_COL_IDX][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
assert!(&aux_hints.op_group_table_hints().is_empty());
assert!(aux_hints.op_group_table_rows().is_empty());
let expected_hints = vec![
(0, BlockTableUpdate::BlockStarted(0)),
(1, BlockTableUpdate::BlockEnded(false)),
];
assert_eq!(expected_hints, aux_hints.block_exec_hints());
let expected_rows = vec![BlockStackTableRow::new_test(INIT_ADDR, ZERO, false)];
assert_eq!(expected_rows, aux_hints.block_stack_table_rows());
let expected_rows = vec![BlockHashTableRow::from_program_hash(program_hash)];
assert_eq!(expected_rows, aux_hints.block_hash_table_rows());
}
#[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, aux_hints, 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!([ZERO; 4], 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!(ONE, trace[OP_BIT_EXTRA_COL_IDX][i]);
assert_eq!(program_hash, get_hasher_state1(&trace, i));
}
assert!(&aux_hints.op_group_table_hints().is_empty());
assert!(aux_hints.op_group_table_rows().is_empty());
let expected_hints = vec![
(0, BlockTableUpdate::BlockStarted(1)),
(1, BlockTableUpdate::BlockStarted(0)),
(4, BlockTableUpdate::BlockEnded(false)),
(5, BlockTableUpdate::LoopRepeated),
(6, BlockTableUpdate::BlockStarted(0)),
(9, BlockTableUpdate::BlockEnded(false)),
(10, BlockTableUpdate::BlockEnded(false)),
];
assert_eq!(expected_hints, aux_hints.block_exec_hints());
let expected_rows = vec![
BlockStackTableRow::new_test(INIT_ADDR, ZERO, true),
BlockStackTableRow::new_test(iter1_addr, INIT_ADDR, false),
BlockStackTableRow::new_test(iter2_addr, INIT_ADDR, false),
];
assert_eq!(expected_rows, aux_hints.block_stack_table_rows());
let expected_rows = vec![
BlockHashTableRow::from_program_hash(program_hash),
BlockHashTableRow::new_test(INIT_ADDR, loop_body_hash, false, true),
];
assert_eq!(expected_rows, aux_hints.block_hash_table_rows());
}
#[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, aux_hints, 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);
let overflow_addr_after_pad = Felt::new(5);
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!([ZERO, ZERO, ZERO, ZERO], 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!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&dec_trace, 7));
assert_eq!(foo_root_hash, get_hasher_state1(&dec_trace, 11));
assert_eq!([ZERO, ZERO, ZERO, ZERO], 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!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&dec_trace, 13));
assert_eq!(last_span_hash, get_hasher_state1(&dec_trace, 16));
assert_eq!([ZERO, ZERO, ZERO, ZERO], 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!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&dec_trace, 17));
for i in 18..trace_len {
assert!(contains_op(&dec_trace, i, Operation::Halt));
assert_eq!(ONE, dec_trace[OP_BIT_EXTRA_COL_IDX][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), [ZERO; 4]);
}
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), [ZERO; 4]);
}
let expected_hints = vec![
(0, BlockTableUpdate::BlockStarted(2)),
(1, BlockTableUpdate::BlockStarted(2)),
(2, BlockTableUpdate::BlockStarted(0)),
(6, BlockTableUpdate::BlockEnded(true)),
(7, BlockTableUpdate::BlockStarted(1)),
(8, BlockTableUpdate::BlockStarted(0)),
(11, BlockTableUpdate::BlockEnded(false)),
(12, BlockTableUpdate::BlockEnded(false)),
(13, BlockTableUpdate::BlockEnded(true)),
(14, BlockTableUpdate::BlockStarted(0)),
(16, BlockTableUpdate::BlockEnded(false)),
(17, BlockTableUpdate::BlockEnded(false)),
];
assert_eq!(expected_hints, aux_hints.block_exec_hints());
let call_ctx =
ExecutionContextInfo::new(0, [ZERO; 4], FMP_MIN + TWO, 17, overflow_addr_after_pad);
let expected_rows = vec![
BlockStackTableRow::new_test(INIT_ADDR, ZERO, false),
BlockStackTableRow::new_test(join1_addr, INIT_ADDR, false),
BlockStackTableRow::new_test(first_span_addr, join1_addr, false),
BlockStackTableRow::new_test_with_ctx(foo_call_addr, join1_addr, false, call_ctx),
BlockStackTableRow::new_test(foo_root_addr, foo_call_addr, false),
BlockStackTableRow::new_test(last_span_addr, INIT_ADDR, false),
];
assert_eq!(expected_rows, aux_hints.block_stack_table_rows());
let expected_rows = vec![
BlockHashTableRow::from_program_hash(program_hash),
BlockHashTableRow::new_test(INIT_ADDR, join1_hash, true, false),
BlockHashTableRow::new_test(INIT_ADDR, last_span_hash, false, false),
BlockHashTableRow::new_test(join1_addr, first_span_hash, true, false),
BlockHashTableRow::new_test(join1_addr, foo_call_hash, false, false),
BlockHashTableRow::new_test(foo_call_addr, foo_root_hash, false, false),
];
assert_eq!(expected_rows, aux_hints.block_hash_table_rows());
}
#[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, aux_hints, 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);
let overflow_addr_after_pad = Felt::new(5);
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!([ZERO, ZERO, ZERO, ZERO], 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!([ZERO, ZERO, ZERO, ZERO], 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!([ZERO, ZERO, ZERO, ZERO], 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!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&dec_trace, 13));
assert_eq!(foo_root_hash, get_hasher_state1(&dec_trace, 17));
assert_eq!([ZERO, ZERO, ZERO, ZERO], 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!([ZERO, ZERO, ZERO, ZERO], 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!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&dec_trace, 21));
assert_eq!(last_span_hash, get_hasher_state1(&dec_trace, 24));
assert_eq!([ZERO, ZERO, ZERO, ZERO], 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!([ZERO, ZERO, ZERO, ZERO], get_hasher_state2(&dec_trace, 25));
for i in 26..trace_len {
assert!(contains_op(&dec_trace, i, Operation::Halt));
assert_eq!(ONE, dec_trace[OP_BIT_EXTRA_COL_IDX][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), [ZERO; 4]);
}
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), [ZERO; 4]);
}
let expected_hints = vec![
(0, BlockTableUpdate::BlockStarted(2)), (1, BlockTableUpdate::BlockStarted(2)), (2, BlockTableUpdate::BlockStarted(0)), (6, BlockTableUpdate::BlockEnded(true)), (7, BlockTableUpdate::BlockStarted(1)), (8, BlockTableUpdate::BlockStarted(2)), (9, BlockTableUpdate::BlockStarted(0)), (12, BlockTableUpdate::BlockEnded(true)), (13, BlockTableUpdate::BlockStarted(1)), (14, BlockTableUpdate::BlockStarted(0)), (17, BlockTableUpdate::BlockEnded(false)), (18, BlockTableUpdate::BlockEnded(false)), (19, BlockTableUpdate::BlockEnded(false)), (20, BlockTableUpdate::BlockEnded(false)), (21, BlockTableUpdate::BlockEnded(true)), (22, BlockTableUpdate::BlockStarted(0)), (24, BlockTableUpdate::BlockEnded(false)), (25, BlockTableUpdate::BlockEnded(false)), ];
assert_eq!(expected_hints, aux_hints.block_exec_hints());
let call_ctx =
ExecutionContextInfo::new(0, [ZERO; 4], FMP_MIN + ONE, 17, overflow_addr_after_pad);
let syscall_ctx = ExecutionContextInfo::new(8, bar_root_hash, FMP_MIN + TWO, 16, ZERO);
let expected_rows = vec![
BlockStackTableRow::new_test(INIT_ADDR, ZERO, false),
BlockStackTableRow::new_test(inner_join_addr, INIT_ADDR, false),
BlockStackTableRow::new_test(first_span_addr, inner_join_addr, false),
BlockStackTableRow::new_test_with_ctx(call_addr, inner_join_addr, false, call_ctx),
BlockStackTableRow::new_test(bar_join_addr, call_addr, false),
BlockStackTableRow::new_test(bar_span_addr, bar_join_addr, false),
BlockStackTableRow::new_test_with_ctx(syscall_addr, bar_join_addr, false, syscall_ctx),
BlockStackTableRow::new_test(syscall_span_addr, syscall_addr, false),
BlockStackTableRow::new_test(last_span_addr, INIT_ADDR, false),
];
assert_eq!(expected_rows, aux_hints.block_stack_table_rows());
let expected_rows = vec![
BlockHashTableRow::from_program_hash(program_hash),
BlockHashTableRow::new_test(INIT_ADDR, inner_join_hash, true, false),
BlockHashTableRow::new_test(INIT_ADDR, last_span_hash, false, false),
BlockHashTableRow::new_test(inner_join_addr, first_span_hash, true, false),
BlockHashTableRow::new_test(inner_join_addr, bar_call_hash, false, false),
BlockHashTableRow::new_test(call_addr, bar_root_hash, false, false),
BlockHashTableRow::new_test(bar_join_addr, bar_span_hash, true, false),
BlockHashTableRow::new_test(bar_join_addr, foo_call_hash, false, false),
BlockHashTableRow::new_test(syscall_addr, foo_root_hash, false, false),
];
assert_eq!(expected_rows, aux_hints.block_hash_table_rows());
}
#[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: &[u64], program: &CodeBlock) -> (DecoderTrace, AuxTraceHints, usize) {
let inputs = ProgramInputs::new(stack, &[], vec![]).unwrap();
let mut process = Process::new(&Kernel::default(), inputs);
process
.execute_code_block(program, &CodeBlockTable::default())
.unwrap();
let (trace, aux_hints) = ExecutionTrace::test_finalize_trace(process);
let trace_len = get_trace_len(&trace) - ExecutionTrace::NUM_RAND_ROWS;
(
trace[DECODER_TRACE_RANGE]
.to_vec()
.try_into()
.expect("failed to convert vector to array"),
aux_hints.decoder,
trace_len,
)
}
fn build_call_trace(
program: &CodeBlock,
fn_block: CodeBlock,
kernel_proc: Option<CodeBlock>,
) -> (SystemTrace, DecoderTrace, AuxTraceHints, usize) {
let kernel = match kernel_proc {
Some(ref proc) => Kernel::new(&[proc.hash()]),
None => Kernel::default(),
};
let inputs = ProgramInputs::new(&[], &[], vec![]).unwrap();
let mut process = Process::new(&kernel, inputs);
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, aux_hints) = ExecutionTrace::test_finalize_trace(process);
let trace_len = get_trace_len(&trace) - ExecutionTrace::NUM_RAND_ROWS;
let sys_trace = trace[SYS_TRACE_RANGE]
.to_vec()
.try_into()
.expect("failed to convert vector to array");
let decoder_trace = trace[DECODER_TRACE_RANGE]
.to_vec()
.try_into()
.expect("failed to convert vector to array");
(sys_trace, decoder_trace, aux_hints.decoder, 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);
assert_eq!(trace[OP_BIT_EXTRA_COL_IDX][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 = [ZERO; 4];
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 = [ZERO; 4];
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 = [ZERO; 4];
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
}
#[allow(dead_code)]
fn print_row(trace: &DecoderTrace, idx: usize) {
let mut row = Vec::new();
for column in trace.iter() {
row.push(column[idx].as_int());
}
println!(
"{}\t{}\t{:?} {} {: <16x?} {: <16x?} {} {}",
idx,
row[0],
&row[OP_BITS_RANGE],
row[8],
&row[9..13],
&row[13..17],
row[17],
row[18]
);
}