-
Notifications
You must be signed in to change notification settings - Fork 66
/
halo2_lib.rs
78 lines (67 loc) · 3.18 KB
/
halo2_lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
use clap::Parser;
use halo2_base::gates::circuit::builder::BaseCircuitBuilder;
use halo2_base::gates::{GateChip, GateInstructions};
use halo2_base::utils::ScalarField;
use halo2_base::AssignedValue;
#[allow(unused_imports)]
use halo2_base::{
Context,
QuantumCell::{Constant, Existing, Witness},
};
use halo2_scaffold::scaffold::cmd::Cli;
use halo2_scaffold::scaffold::run;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CircuitInput {
pub x: String, // field element, but easier to deserialize as a string
}
// this algorithm takes a public input x, computes x^2 + 72, and outputs the result as public output
fn some_algorithm_in_zk<F: ScalarField>(
builder: &mut BaseCircuitBuilder<F>,
input: CircuitInput,
make_public: &mut Vec<AssignedValue<F>>,
) {
let x = F::from_str_vartime(&input.x).expect("deserialize field element should not fail");
// `Context` can roughly be thought of as a single-threaded execution trace of a program we want to ZK prove. We do some post-processing on `Context` to optimally divide the execution trace into multiple columns in a PLONKish arithmetization
let ctx = builder.main(0);
// More advanced usage with multi-threaded witness generation is possible, but we do not explain it here
// first we load a number `x` into as system, as a "witness"
let x = ctx.load_witness(x);
// by default, all numbers in the system are private
// we can make it public like so:
make_public.push(x);
// create a Gate chip that contains methods for basic arithmetic operations
let gate = GateChip::<F>::default();
// ===== way 1 =====
// now we can perform arithmetic operations almost like a normal program using halo2-lib API functions
// square x
let x_sq = gate.mul(ctx, x, x);
// x^2 + 72
let c = F::from(72);
// the implicit type of most variables is an "Existing" assigned value
// a known constant is a separate type that we specify by `Constant(c)`:
let out = gate.add(ctx, x_sq, Constant(c));
// Halo2 does not distinguish between public inputs vs outputs because the verifier seems them all at the same time
// However in traditional terms, `out` is our output number. It is currently still private.
// Let's make it public:
make_public.push(out);
// ==== way 2 =======
// here is a more optimal way to compute x^2 + 72 using the lower level `assign_region` API:
let val = *x.value() * x.value() + c;
let _val_assigned =
ctx.assign_region_last([Constant(c), Existing(x), Existing(x), Witness(val)], [0]);
// the `[0]` tells us to turn on a vertical `a + b * c = d` gate at row position 0.
// this imposes the constraint c + x * x = val
// ==== way 3 ======
// this does the exact same thing as way 2, but with a pre-existing function
let _val_assigned = gate.mul_add(ctx, x, x, Constant(c));
println!("x: {:?}", x.value());
println!("val_assigned: {:?}", out.value());
assert_eq!(*x.value() * x.value() + c, *out.value());
}
fn main() {
env_logger::init();
let args = Cli::parse();
// run different zk commands based on the command line arguments
run(some_algorithm_in_zk, args);
}