sp1_core_machine/syscall/instructions/
mod.rs

1use columns::NUM_SYSCALL_INSTR_COLS;
2use p3_air::BaseAir;
3
4pub mod air;
5pub mod columns;
6pub mod trace;
7
8#[derive(Default)]
9pub struct SyscallInstrsChip;
10
11impl<F> BaseAir<F> for SyscallInstrsChip {
12    fn width(&self) -> usize {
13        NUM_SYSCALL_INSTR_COLS
14    }
15}
16
17#[cfg(test)]
18mod tests {
19    use std::borrow::BorrowMut;
20
21    use p3_baby_bear::BabyBear;
22    use p3_field::AbstractField;
23    use p3_matrix::dense::RowMajorMatrix;
24    use sp1_core_executor::{ExecutionRecord, Instruction, Opcode, Program};
25    use sp1_stark::{
26        air::MachineAir, baby_bear_poseidon2::BabyBearPoseidon2, chip_name, CpuProver,
27        MachineProver, Val,
28    };
29    use sp1_zkvm::syscalls::{COMMIT, COMMIT_DEFERRED_PROOFS, HALT, SHA_EXTEND};
30
31    use crate::{
32        cpu::{columns::CpuCols, CpuChip},
33        io::SP1Stdin,
34        riscv::RiscvAir,
35        syscall::instructions::{columns::SyscallInstrColumns, SyscallInstrsChip},
36        utils::run_malicious_test,
37    };
38
39    #[test]
40    fn test_malicious_next_pc() {
41        struct TestCase {
42            program: Vec<Instruction>,
43            incorrect_next_pc: u32,
44        }
45
46        let test_cases = vec![
47            TestCase {
48                program: vec![
49                    Instruction::new(Opcode::ADD, 5, 0, HALT, false, true), /* Set the syscall
50                                                                             * code in register
51                                                                             * x5. */
52                    Instruction::new(Opcode::ECALL, 5, 10, 11, false, false), // Call the syscall.
53                    Instruction::new(Opcode::ADD, 30, 0, 100, false, true),
54                ],
55                incorrect_next_pc: 8, // The correct next_pc is 0.
56            },
57            TestCase {
58                program: vec![
59                    Instruction::new(Opcode::ADD, 5, 0, SHA_EXTEND, false, true), /* Set the syscall code in register x5. */
60                    Instruction::new(Opcode::ADD, 10, 0, 40, false, true),        /* Set the syscall
61                                                                                   * arg1 to 40. */
62                    Instruction::new(Opcode::ECALL, 5, 10, 11, false, false), // Call the syscall.
63                    Instruction::new(Opcode::ADD, 30, 0, 100, false, true),
64                ],
65                incorrect_next_pc: 0, // The correct next_pc is 12.
66            },
67        ];
68
69        for test_case in test_cases {
70            let program = Program::new(test_case.program, 0, 0);
71            let stdin = SP1Stdin::new();
72
73            type P = CpuProver<BabyBearPoseidon2, RiscvAir<BabyBear>>;
74
75            let malicious_trace_pv_generator =
76                move |prover: &P,
77                      record: &mut ExecutionRecord|
78                      -> Vec<(String, RowMajorMatrix<Val<BabyBearPoseidon2>>)> {
79                    // Create a malicious record where the next pc is set to the incorrect value.
80                    let mut malicious_record = record.clone();
81
82                    // There can be multiple shards for programs with syscalls, so need to figure
83                    // out which record is for a CPU shard.
84                    if !malicious_record.cpu_events.is_empty() {
85                        malicious_record.syscall_events[0].next_pc = test_case.incorrect_next_pc;
86                    }
87
88                    prover.generate_traces(&malicious_record)
89                };
90
91            let result =
92                run_malicious_test::<P>(program, stdin, Box::new(malicious_trace_pv_generator));
93            let syscall_chip_name = chip_name!(SyscallInstrsChip, BabyBear);
94            assert!(
95                result.is_err() && result.unwrap_err().is_constraints_failing(&syscall_chip_name)
96            );
97        }
98    }
99
100    #[test]
101    fn test_malicious_extra_cycles() {
102        let instructions = vec![
103            Instruction::new(Opcode::ADD, 5, 0, SHA_EXTEND, false, true), /* Set the syscall
104                                                                           * code in register
105                                                                           * x5. */
106            Instruction::new(Opcode::ADD, 10, 0, 40, false, true), // Set the syscall arg1 to 40.
107            Instruction::new(Opcode::ECALL, 5, 10, 11, false, false), // Call the syscall.
108            Instruction::new(Opcode::ADD, 30, 20, 100, true, true),
109        ];
110        let program = Program::new(instructions, 0, 0);
111        let stdin = SP1Stdin::new();
112
113        type P = CpuProver<BabyBearPoseidon2, RiscvAir<BabyBear>>;
114
115        let malicious_trace_pv_generator =
116            |prover: &P,
117             record: &mut ExecutionRecord|
118             -> Vec<(String, RowMajorMatrix<Val<BabyBearPoseidon2>>)> {
119                let mut traces = prover.generate_traces(record);
120
121                let cpu_chip_name = chip_name!(CpuChip, BabyBear);
122                let syscall_chip_name = chip_name!(SyscallInstrsChip, BabyBear);
123
124                for (chip_name, trace) in traces.iter_mut() {
125                    if *chip_name == cpu_chip_name {
126                        let third_row = trace.row_mut(2);
127                        let third_row: &mut CpuCols<BabyBear> = third_row.borrow_mut();
128                        assert!(third_row.is_syscall == BabyBear::one());
129                        third_row.num_extra_cycles = BabyBear::from_canonical_usize(8);
130                        // Correct value is 48.
131
132                        let fourth_row = trace.row_mut(3);
133                        let fourth_row: &mut CpuCols<BabyBear> = fourth_row.borrow_mut();
134                        fourth_row.clk_16bit_limb = BabyBear::from_canonical_usize(20);
135                        // Correct value is 60.
136                    }
137
138                    if *chip_name == syscall_chip_name {
139                        let first_row = trace.row_mut(0);
140                        let first_row: &mut SyscallInstrColumns<BabyBear> = first_row.borrow_mut();
141                        first_row.num_extra_cycles = BabyBear::from_canonical_usize(4);
142                        // Correct value is 48.
143                    }
144                }
145
146                traces
147            };
148
149        let result =
150            run_malicious_test::<P>(program, stdin, Box::new(malicious_trace_pv_generator));
151        let syscall_chip_name = chip_name!(SyscallInstrsChip, BabyBear);
152        assert!(result.is_err() && result.unwrap_err().is_constraints_failing(&syscall_chip_name));
153    }
154
155    #[test]
156    fn test_malicious_commit() {
157        let instructions = vec![
158            Instruction::new(Opcode::ADD, 5, 0, COMMIT, false, true), /* Set the syscall code in
159                                                                       * register x5. */
160            Instruction::new(Opcode::ADD, 10, 0, 0, false, false), /* Set the syscall code in
161                                                                    * register x5. */
162            Instruction::new(Opcode::ADD, 11, 0, 40, false, true), // Set the syscall arg1 to 40.
163            Instruction::new(Opcode::ECALL, 5, 10, 11, false, false), // Call the syscall.
164        ];
165        let program = Program::new(instructions, 0, 0);
166        let stdin = SP1Stdin::new();
167
168        type P = CpuProver<BabyBearPoseidon2, RiscvAir<BabyBear>>;
169
170        let malicious_trace_pv_generator =
171            |prover: &P,
172             record: &mut ExecutionRecord|
173             -> Vec<(String, RowMajorMatrix<Val<BabyBearPoseidon2>>)> {
174                record.public_values.committed_value_digest[0] = 10; // The correct value is 40.
175                prover.generate_traces(record)
176            };
177
178        let result =
179            run_malicious_test::<P>(program, stdin, Box::new(malicious_trace_pv_generator));
180        let syscall_chip_name = chip_name!(SyscallInstrsChip, BabyBear);
181        assert!(result.is_err() && result.unwrap_err().is_constraints_failing(&syscall_chip_name));
182    }
183
184    #[test]
185    fn test_malicious_commit_deferred() {
186        let instructions = vec![
187            Instruction::new(Opcode::ADD, 5, 0, COMMIT_DEFERRED_PROOFS, false, true), /* Set the
188                                                                                       * syscall
189                                                                                       * code in
190                                                                                       * register
191                                                                                       * x5. */
192            Instruction::new(Opcode::ADD, 10, 0, 0, false, false), /* Set the syscall code in
193                                                                    * register x5. */
194            Instruction::new(Opcode::ADD, 11, 0, 40, false, true), // Set the syscall arg1 to 40.
195            Instruction::new(Opcode::ECALL, 5, 10, 11, false, false), // Call the syscall.
196        ];
197        let program = Program::new(instructions, 0, 0);
198        let stdin = SP1Stdin::new();
199
200        type P = CpuProver<BabyBearPoseidon2, RiscvAir<BabyBear>>;
201
202        let malicious_trace_pv_generator =
203            |prover: &P,
204             record: &mut ExecutionRecord|
205             -> Vec<(String, RowMajorMatrix<Val<BabyBearPoseidon2>>)> {
206                record.public_values.deferred_proofs_digest[0] = 10; // The correct value is 40.
207                prover.generate_traces(record)
208            };
209
210        let result =
211            run_malicious_test::<P>(program, stdin, Box::new(malicious_trace_pv_generator));
212        let syscall_chip_name = chip_name!(SyscallInstrsChip, BabyBear);
213        assert!(result.is_err() && result.unwrap_err().is_constraints_failing(&syscall_chip_name));
214    }
215}