Types
All of the types relevant to a ZKP program live in
sunscreen::types::zkp
.
Native field elements
The ZKPs we're looking at operate over mathematical objects called fields. Thus, we're actually specifying field elements in our ZKP programs.
As we saw in my first ZKP program, the atomic
type used in all ZKP programs is Field<F>
, which represents a native
field element in the backend proof system. For most users, this is
the only type you need to be aware of!
The Field
type supports the following basic field arithmetic: +
, -
, *
.1
#![allow(unused)] fn main() { use sunscreen::{ types::zkp::{ConstrainEq, Field, FieldSpec}, zkp_program }; #[zkp_program] fn distribution<F: FieldSpec>(a: Field<F>, b: Field<F>, c: Field<F>) { let left = a * (b + c); let right = a * b + a * c; left.constrain_eq(right); } }
The constrain_eq
introduces a constraint into the proof.
We'll talk more about this in Constraints.
If you're familiar with fields but haven't worked with ZKPs before you may be a little confused—don't fields support division? Yes, they do. However, in ZKPs, we're thinking about constraints in our programs. Division is not "native" to R1CS constraints so it is not supported out of the box.
Arrays
Sunscreen supports fixed-length arrays2 that behave as you'd expect. You declare and use them like any other fixed-length array in Rust:
#![allow(unused)] fn main() { use sunscreen::{ types::zkp::{ConstrainEq, Field, FieldSpec}, zkp_program }; #[zkp_program] fn my_program<F: FieldSpec>(a: [[Field<F>; 10]; 10], b: [Field<F>; 10]) { // Assert that each array in `a` is equal to the `b` array for i in 0..10 { for j in 0..10 { a[i][j].constrain_eq(b[j]); } } } }
Don't confuse these with Vec
, which Sunscreen does not support!
Working with literals
Sometimes, you simply want to double a value or add 15
. The easiest way to do
this is to use the
sunscreen::zkp_var!
macro:
#![allow(unused)] fn main() { use sunscreen::{ types::zkp::{ConstrainEq, Field, FieldSpec}, zkp_program, zkp_var }; #[zkp_program] fn double_and_add<F: FieldSpec>(a: Field<F>) { let two = zkp_var!(2); let x = two * a + zkp_var!(15); x.constrain_eq(zkp_var!(42)); } }
You can also create arrays:
#![allow(unused)] fn main() { use sunscreen::{ types::zkp::{ConstrainEq, Field, FieldSpec}, zkp_program, zkp_var }; #[zkp_program] fn my_program<F: FieldSpec>(a: [Field<F>; 10]) { // Assert a is all zeroes. let arr = zkp_var![0; 5]; for i in 0..5 { arr[i].constrain_eq(a[i]); } // You can also specify varying values, like normal array syntax: let arr = zkp_var![11, 1, 2, 42]; for i in 0..5 { arr[i].constrain_eq(a[i + 5]); } } }