Skip to main content

sp1_lib/
io.rs

1#![allow(unused_unsafe)]
2use crate::{halt_invalid_hint, read_vec_raw, syscall_write, ReadVecResult};
3use serde::{de::DeserializeOwned, Serialize};
4use std::io::{Result, Write};
5
6pub use sp1_primitives::consts::fd::*;
7
8/// A writer that writes to a file descriptor inside the zkVM.
9struct SyscallWriter {
10    fd: u32,
11}
12
13impl Write for SyscallWriter {
14    fn write(&mut self, buf: &[u8]) -> Result<usize> {
15        let nbytes = buf.len();
16        let write_buf = buf.as_ptr();
17        unsafe {
18            syscall_write(self.fd, write_buf, nbytes);
19        }
20        Ok(nbytes)
21    }
22
23    fn flush(&mut self) -> Result<()> {
24        Ok(())
25    }
26}
27
28/// Read a buffer from the input stream. The buffer is read into uninitialized memory.
29///
30/// When the `bump` feature is enabled, the buffer is read into a new buffer allocated by the
31/// program.
32///
33/// When the `embedded` feature is enabled, the buffer is read into the reserved input region.
34///
35/// When there is no allocator selected, the program will fail to compile.
36///
37/// ### Examples
38/// ```ignore
39/// let data: Vec<u8> = sp1_zkvm::io::read_vec();
40/// ```
41#[track_caller]
42pub fn read_vec() -> Vec<u8> {
43    let ReadVecResult { ptr, len, capacity } = unsafe { read_vec_raw() };
44
45    if ptr.is_null() {
46        empty_input_stream_halt();
47    }
48
49    unsafe { Vec::from_raw_parts(ptr, len, capacity) }
50}
51
52/// The input stream is prover-controlled, so an empty read is an invalid-hint
53/// condition rather than a regular panic. We print the same diagnostic message
54/// to stderr (FD 2) that `panic!` would have, then halt with exit code 3 so a
55/// malicious prover cannot forge a regular panic (exit code 1) by withholding
56/// hint data.
57#[track_caller]
58#[cold]
59#[inline(never)]
60fn empty_input_stream_halt() -> ! {
61    let msg = std::format!(
62        "Tried to read from the input stream, but it was empty @ {}\n\
63         Was the correct data written into SP1Stdin?\n\
64         Halting with exit code 3 (invalid prover hint).\n",
65        std::panic::Location::caller()
66    );
67    unsafe {
68        syscall_write(2, msg.as_ptr(), msg.len());
69    }
70    halt_invalid_hint()
71}
72
73/// Read a deserializable object from the input stream.
74///
75/// ### Examples
76/// ```ignore
77/// use serde::{Deserialize, Serialize};
78///
79/// #[derive(Serialize, Deserialize)]
80/// struct MyStruct {
81///     a: u32,
82///     b: u32,
83/// }
84///
85/// let data: MyStruct = sp1_zkvm::io::read();
86/// ```
87#[track_caller]
88pub fn read<T: DeserializeOwned>() -> T {
89    let ReadVecResult { ptr, len, capacity } = unsafe { read_vec_raw() };
90
91    if ptr.is_null() {
92        empty_input_stream_halt();
93    }
94
95    // 1. `ptr` was allocated using alloc
96    // 2. Assume that the allocator in the VM doesn't deallocate in the input space.
97    // 3. Size and length are correct from above. Length is <= capacity.
98    let vec = unsafe { Vec::from_raw_parts(ptr, len, capacity) };
99
100    // bincode bytes are also prover-controlled — a corrupt encoding is an
101    // invalid hint, not a regular panic.
102    match bincode::deserialize(&vec) {
103        Ok(v) => v,
104        Err(_) => halt_invalid_hint(),
105    }
106}
107
108/// Commit a serializable object to the public values stream.
109///
110/// ### Examples
111/// ```ignore
112/// use serde::{Deserialize, Serialize};
113///
114/// #[derive(Serialize, Deserialize)]
115/// struct MyStruct {
116///     a: u32,
117///     b: u32,
118/// }
119///
120/// let data = MyStruct {
121///     a: 1,
122///     b: 2,
123/// };
124/// sp1_zkvm::io::commit(&data);
125/// ```
126pub fn commit<T: Serialize>(value: &T) {
127    let writer = SyscallWriter { fd: FD_PUBLIC_VALUES };
128    bincode::serialize_into(writer, value).expect("serialization failed");
129}
130
131/// Commit bytes to the public values stream.
132///
133/// ### Examples
134/// ```ignore
135/// let data = vec![1, 2, 3, 4];
136/// sp1_zkvm::io::commit_slice(&data);
137/// ```
138pub fn commit_slice(buf: &[u8]) {
139    let mut my_writer = SyscallWriter { fd: FD_PUBLIC_VALUES };
140    my_writer.write_all(buf).unwrap();
141}
142
143/// Hint a serializable object to the hint stream.
144///
145/// ### Examples
146/// ```ignore
147/// use serde::{Deserialize, Serialize};
148///
149/// #[derive(Serialize, Deserialize)]
150/// struct MyStruct {
151///     a: u32,
152///     b: u32,
153/// }
154///
155/// let data = MyStruct {
156///     a: 1,
157///     b: 2,
158/// };
159/// sp1_zkvm::io::hint(&data);
160/// ```
161pub fn hint<T: Serialize>(value: &T) {
162    let writer = SyscallWriter { fd: FD_HINT };
163    bincode::serialize_into(writer, value).expect("serialization failed");
164}
165
166/// Hint bytes to the hint stream.
167///
168/// ### Examples
169/// ```ignore
170/// let data = vec![1, 2, 3, 4];
171/// sp1_zkvm::io::hint_slice(&data);
172/// ```
173pub fn hint_slice(buf: &[u8]) {
174    let mut my_reader = SyscallWriter { fd: FD_HINT };
175    my_reader.write_all(buf).unwrap();
176}
177
178/// Write the data `buf` to the file descriptor `fd`.
179///
180/// ### Examples
181/// ```ignore
182/// let data = vec![1, 2, 3, 4];
183/// sp1_zkvm::io::write(3, &data);
184/// ```
185pub fn write(fd: u32, buf: &[u8]) {
186    SyscallWriter { fd }.write_all(buf).unwrap();
187}