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}