use alloc::string::String;
use rstest::fixture;
use super::*;
#[rstest]
#[case(Some("pub proc foo add end"), "begin push.1 syscall.foo swap.8 drop end", vec![Felt::from_u32(16); 16])]
#[case(Some("pub proc foo caller end"), "begin syscall.foo end", vec![Felt::from_u32(16); 16])]
#[case(Some("pub proc foo caller end"), "proc bar syscall.foo end begin call.bar end", vec![Felt::from_u32(16); 16])]
#[case(Some("pub proc foo clk add end"), "begin syscall.foo end", vec![Felt::from_u32(16); 16])]
#[case(Some("@locals(2) pub proc foo locaddr.0 locaddr.1 swap.8 drop swap.8 drop end"), "proc bar syscall.foo end begin call.bar end", vec![Felt::from_u32(16); 16])]
#[case(Some("pub proc foo add end"), "proc bar push.100 mem_store.44 syscall.foo mem_load.44 swap.8 drop end begin call.bar end", vec![Felt::from_u32(16); 16])]
#[case(Some("pub proc foo push.100 mem_store.44 end pub proc baz mem_load.44 swap.8 drop end"),
"proc bar
syscall.foo syscall.baz
end
begin call.bar end",
vec![Felt::from_u32(16); 16]
)]
#[case(None, "proc foo add end begin push.1 call.foo swap.8 drop end", vec![Felt::from_u32(16); 16])]
#[case(None, "
proc foo clk add end
begin push.1
if.true call.foo else swap end
clk swap.8 drop
end",
vec![Felt::from_u32(16); 16]
)]
#[case(None,"
@locals(2) proc foo locaddr.0 locaddr.1 swap.8 drop swap.8 drop end
begin call.foo end",
vec![Felt::from_u32(16); 16]
)]
#[case(None,"
proc foo push.100 mem_store.44 end
proc bar mem_load.44 assertz end
begin call.foo mem_load.44 assertz call.bar end",
vec![Felt::from_u32(16); 16]
)]
#[case(None,"
proc foo mem_load.44 assertz end
proc bar push.100 mem_store.44 call.foo mem_load.44 swap.8 drop end
begin call.bar end",
vec![Felt::from_u32(16); 16]
)]
#[case(None, "
proc foo add end
begin
procref.foo mem_storew_le.100 dropw push.100
dyncall swap.8 drop
end",
vec![Felt::from_u32(16); 16]
)]
#[case(None, "
proc foo clk add end
begin
push.1
if.true
procref.foo mem_storew_le.100 dropw
push.100 dyncall
push.100 dyncall
else
swap
end
clk swap.8 drop
end",
vec![Felt::from_u32(16); 16]
)]
#[case(None,"
@locals(2) proc foo locaddr.0 locaddr.1 swap.8 drop swap.8 drop end
begin
procref.foo mem_storew_le.100 dropw push.100
dyncall
end",
vec![Felt::from_u32(16); 16]
)]
#[case(None,"
proc foo push.100 mem_store.44 end
proc bar mem_load.44 assertz end
begin
procref.foo mem_storew_le.100 dropw push.100 dyncall
mem_load.44 assertz
procref.bar mem_storew_le.104 dropw push.104 dyncall
end",
vec![Felt::from_u32(16); 16]
)]
#[case(None,"
proc foo mem_load.44 assertz end
proc bar
push.100 mem_store.44
procref.foo mem_storew_le.104 dropw push.104 dyncall
mem_load.44 swap.8 drop
end
begin
procref.bar mem_storew_le.104 dropw push.104 dyncall
end",
vec![Felt::from_u32(16); 16]
)]
#[case(None, "
proc foo add end
begin
procref.foo mem_storew_le.100 dropw push.100
dynexec swap.8 drop
end",
vec![Felt::from_u32(16); 16]
)]
#[case(None, "
proc foo clk add end
begin
push.1
if.true
procref.foo mem_storew_le.100 dropw
push.100 dynexec
push.100 dynexec
else
swap
end
clk swap.8 drop
end",
vec![Felt::from_u32(16); 16]
)]
#[case(None,"
@locals(2) proc foo locaddr.0 locaddr.1 swap.8 drop swap.8 drop end
begin
procref.foo mem_storew_le.100 dropw push.100
dynexec
end",
vec![Felt::from_u32(16); 16]
)]
#[case(None,"
proc foo push.100 mem_store.44 end
proc bar mem_load.44 sub.100 assertz end
begin
procref.foo mem_storew_le.104 dropw push.104 dynexec
mem_load.44 sub.100 assertz
procref.bar mem_storew_le.108 dropw push.108 dynexec
end",
vec![Felt::from_u32(16); 16]
)]
#[case(None, "begin while.true push.1 assertz end clk swap.8 drop end", vec![ZERO, Felt::from_u32(1), Felt::from_u32(2), Felt::from_u32(3)])]
#[case(None,
"begin
while.true
clk swap.15 drop
end
clk swap.8 drop
end",
vec![ONE, ONE, ONE, ONE, ZERO, Felt::from_u32(42)]
)]
#[case(None,
"begin
push.1.2.3.4 mem_storew_le.40 dropw
horner_eval_base
end",
vec![Felt::from_u32(16), Felt::from_u32(15), Felt::from_u32(14), Felt::from_u32(13), Felt::from_u32(12), Felt::from_u32(11), Felt::from_u32(10),
Felt::from_u32(9), Felt::from_u32(8), Felt::from_u32(7), Felt::from_u32(6), Felt::from_u32(5), Felt::from_u32(4),
Felt::from_u32(40), Felt::from_u32(4), Felt::from_u32(100)]
)]
#[case(None,
"begin
push.1.2.3.4 mem_storew_le.40 dropw
horner_eval_ext
end",
vec![Felt::from_u32(16), Felt::from_u32(15), Felt::from_u32(14), Felt::from_u32(13), Felt::from_u32(12), Felt::from_u32(11), Felt::from_u32(10),
Felt::from_u32(9), Felt::from_u32(8), Felt::from_u32(7), Felt::from_u32(6), Felt::from_u32(5), Felt::from_u32(4),
Felt::from_u32(40), Felt::from_u32(4), Felt::from_u32(100)]
)]
#[case(None, "begin log_precompile end",
vec![Felt::from_u32(1), Felt::from_u32(2), Felt::from_u32(3), Felt::from_u32(4),
Felt::from_u32(5), Felt::from_u32(6), Felt::from_u32(7), Felt::from_u32(8)],
)]
#[case(None,"
begin
u32divmod
end",
vec![Felt::from_u32(6), Felt::from_u32(3)]
)]
#[case(None,"
begin
u32overflowing_add sub.1 assertz
end",
vec![Felt::from_u32(u32::MAX), ONE]
)]
fn test_masm_consistency(
testname: String,
#[case] kernel_source: Option<&'static str>,
#[case] program_source: &'static str,
#[case] stack_inputs: Vec<Felt>,
) {
let (program, kernel_lib) = {
let source_manager = Arc::new(DefaultSourceManager::default());
match kernel_source {
Some(kernel_source) => {
let kernel_lib =
Assembler::new(source_manager.clone()).assemble_kernel(kernel_source).unwrap();
let program = Assembler::with_kernel(source_manager, kernel_lib.clone())
.assemble_program(program_source)
.unwrap();
(program, Some(kernel_lib))
},
None => {
let program =
Assembler::new(source_manager).assemble_program(program_source).unwrap();
(program, None)
},
}
};
let mut host = DefaultHost::default();
if let Some(kernel_lib) = &kernel_lib {
host.load_library(kernel_lib.mast_forest()).unwrap();
}
let processor = FastProcessor::new(StackInputs::new(&stack_inputs).unwrap());
let fast_stack_outputs = processor.execute_sync(&program, &mut host).unwrap().stack;
let stepped_stack_outputs = {
let processor = FastProcessor::new(StackInputs::new(&stack_inputs).unwrap());
processor.execute_by_step_sync(&program, &mut host).unwrap()
};
assert_eq!(fast_stack_outputs, stepped_stack_outputs);
insta::assert_debug_snapshot!(testname, fast_stack_outputs);
}
#[rstest]
#[case(None, "begin while.true swap end end", vec![Felt::from_u32(2); 16])]
#[case(None, "begin while.true push.100 end end", vec![ONE; 16])]
#[case(None,"
begin
dyncall
end",
vec![Felt::from_u32(16); 16]
)]
#[case(None,"
begin
dynexec
end",
vec![Felt::from_u32(16); 16]
)]
#[case(None,"
begin
u32divmod
end",
vec![ZERO; 16]
)]
#[case(None,"
begin
u32overflowing_add
end",
vec![Felt::from_u32(u32::MAX) + ONE, ZERO]
)]
fn test_masm_errors_consistency(
testname: String,
#[case] kernel_source: Option<&'static str>,
#[case] program_source: &'static str,
#[case] stack_inputs: Vec<Felt>,
) {
let (program, kernel_lib) = {
let source_manager = Arc::new(DefaultSourceManager::default());
match kernel_source {
Some(kernel_source) => {
let kernel_lib =
Assembler::new(source_manager.clone()).assemble_kernel(kernel_source).unwrap();
let program = Assembler::with_kernel(source_manager, kernel_lib.clone())
.assemble_program(program_source)
.unwrap();
(program, Some(kernel_lib))
},
None => {
let program =
Assembler::new(source_manager).assemble_program(program_source).unwrap();
(program, None)
},
}
};
let mut host = DefaultHost::default();
if let Some(kernel_lib) = &kernel_lib {
host.load_library(kernel_lib.mast_forest()).unwrap();
}
let processor = FastProcessor::new(StackInputs::new(&stack_inputs).unwrap());
let fast_err = processor.execute_sync(&program, &mut host).unwrap_err();
let fast_stepped_err = {
let processor = FastProcessor::new(StackInputs::new(&stack_inputs).unwrap());
processor.execute_by_step_sync(&program, &mut host).unwrap_err()
};
assert_eq!(fast_err.to_string(), fast_stepped_err.to_string());
insta::assert_debug_snapshot!(testname, fast_err);
}
#[test]
fn test_log_precompile_correctness() {
use miden_core::crypto::hash::Poseidon2;
let stack_inputs = [1, 2, 3, 4, 5, 6, 7, 8].map(Felt::new);
let comm_calldata: Word = [1, 2, 3, 4].map(Felt::new).into();
let tag: Word = [5, 6, 7, 8].map(Felt::new).into();
let cap_prev = Word::empty();
let mut hasher_state = [ZERO; 12];
hasher_state[0..4].copy_from_slice(comm_calldata.as_slice());
hasher_state[4..8].copy_from_slice(tag.as_slice());
hasher_state[8..12].copy_from_slice(cap_prev.as_slice());
Poseidon2::apply_permutation(&mut hasher_state);
let expected_r0: Word = hasher_state[0..4].try_into().unwrap();
let expected_r1: Word = hasher_state[4..8].try_into().unwrap();
let expected_cap: Word = hasher_state[8..12].try_into().unwrap();
let program_source = "begin log_precompile end";
let program = {
let source_manager = Arc::new(DefaultSourceManager::default());
Assembler::new(source_manager).assemble_program(program_source).unwrap()
};
let mut host = DefaultHost::default();
let processor = FastProcessor::new(StackInputs::new(&stack_inputs).unwrap());
let execution_output = processor.execute_sync(&program, &mut host).unwrap();
let actual_r0 = execution_output.stack.get_word(0).unwrap();
let actual_r1 = execution_output.stack.get_word(4).unwrap();
let actual_cap = execution_output.stack.get_word(8).unwrap();
assert_eq!(expected_r0, actual_r0, "R0 mismatch");
assert_eq!(expected_r1, actual_r1, "R1 mismatch");
assert_eq!(expected_cap, actual_cap, "CAP_NEXT mismatch");
}
#[fixture]
fn testname() -> String {
std::thread::current().name().unwrap().replace("::", "__")
}