 I'm CC from PSE. Today I'm sharing some of my developer experience for programming the circuits, ZK circuits. So we know in ZK Snark, well, we allow the prover and the verifier to agree on the algorithm. And then prover can provide a proof, input, and output to the verifier. And then verifier can be convinced that the computation, the output, is correct corresponding to the input without re-computing the algorithm again. So lots of scaling and privacy. We are getting from the Snark. And I'm going to unroll this process more. It's not a full story, but you as a circuit developer, you are on the left-hand side. You define some constraints, and then you send a verifying key and proving key generated from these constraints, and send it to a verifier and the prover. We call this setup time. This is when you finish the circuit development and deploy the project and setup is ready. But at a proving time, whenever you need to send a proof, so prover would run the computation and get the computation traces and fielding to the circuits. And using the proving key to generate a proof and then send the proof to the verifier. Today, we don't care about how sausage is made. We care about how do you turn the computation into circuits. So if you're coming from a normal programming world, for example, if you write Python, C, Rust, anything, your brain works with function, loops, and evals, this kind of good stuff. But when you enter a circuit world, you need to do the algorithmatization. And you need to get the circuit trace. So you're thinking in the computation trace of the whole computation. And you think of to verifying these computations with math and equations. So it's like that. So before we go into introduce some tricks and development tips that talk about the rules of the game, like how does a circuit thing work. So first of all, all computation is represented in finite field arithmetic. So a finite field element is, you can think it as an integer. And it's a positive integer and less than a number p. And let's say if p equals to 3, then if you have two field elements, two and two, you're adding together, you actually got a result of one because we need to do it in module of 3, module of p. And p is usually a very large number. For example, it's 254 bits. So the takeaway is that we need to represent your computation in field numbers instead of bits and bytes. And you need to watch out for the web over and over for all the stuff. And then we get this grid of stuff, like a paper. You can fill in the field numbers inside those cells. And if you can expand this grid with more columns, but for every new column you added, it would be more costly. But the rows are basically free, but they are kept up to some limit. Let's say 2 to the 18, like this is 260k, roughly. So ideally, you want to use as much row as possible until maybe you have more computation you need to do. Then you add more columns and add proving calls, coding times, verifying calls, something like that. And we have to distinguish some types of columns. These columns are distinguished by who can see what and when values are assigned. So the pink one is the advice column. It is only visible to prove it. It is determined at a proving time to prove a witness value inside this column. We have a fixed column. A fixed column is assigned at a set up time. So both prover and verifier have a copy. And then the instance column. So the value is visible to verifier. So the prover can fill in the input and output in a proving time so that we can have the scenario in the first slide. That the prover witness some private values and then some public instance values for the input and the output using the proving key to generate a proof. And then verifier can verify the proof and also take the 10 and 100 layer. You can verify it in the country or do other verification, if you may. OK, so now we have the grid. We have the columns. We need to define what values in the cells are correct or valid, like what values are allowed to put in the cells. So these are constraints. And I'm going to introduce two types of constraints in HALO2. The first one is the custom gates. So this defines the constraint in polynomials. So the gate looks like this. We choose the cells from column A, the cells from column B, and the cells from column C. You can choose whatever column you want and you can choose the relative position. For example, I want the next column in relative to the cell. And then you can define a polynomial to constraint like, OK, A must equal to the B plus the next cell of the B. But this gate will be applied to all rows in the column. So sometimes we don't want to do that. So that's why we have a selector here. So this selector will be the 1 or 0 value. So if this selector is 1, then this expression here must equal to 0. Then our constraint is enforced. But if selector is 0, then the whole expression is already 0. So this constraint does not necessarily hold for that particular role. I just want to quote the sentence from Vitalik I heard from his talk. He said, like, everything is a polynomial. I'm a polynomial. So to give an example, a viral example from Intone's Fibonacci circuit example. And we want to prove the nth term of the Fibonacci number. And then we can fill in the value of 1, 1, 2, 3, 5, everything into the grid. And we define the gates that left plus right must equal to a sum. That means that left plus right minus sum equals to 0. And we use the selector Q-Fib to determine like which role we want to enforce this constraint. So here is one way to do it. Like we make this gate looks very straight. They are on the same rows. But you can see that we are using three columns here. So this is the other way you can do it. You can make it like the sum. You can fold it to the next cell. And it uses less columns. And but it works the same way. It still can give you your desired computation result. So it's flexible. You can determine the shape of the gates. Depends on your problem to solve. And the second tool to constraint the grid value is copy constraints. So I have this yellow tab here to glue the values of two cells together. And if they are glued, so the value assigned in the cells must equal. And the glue must be determined at a set of time. There is no way you can change it at a proving time. But it's very cheap and use it as much as possible. The final rule is that the prover can be evil. I like to think prover or everything with a prover, verify a miner, things like that. As a cyborg, they are the human who runs the machine. And the machine runs the algorithm. But the human are attracted by incentives. And they could do all kind of stuff. So if you didn't write your circuit right, prover can witness the wrong values and still convince the verifier to do stuff. So if you are designing a decentralized mixer for this and prove a witness some value, they can withdraw the money out of the air, then your project is hacked. So let's get into tricks. Let's start from the simple one. How do we limit the options in one cell? For example, I want this cell to allow only values 1, or 2, or 3, nothing else. So to do this, we can define a gate that has this expression that's cell value minus 1 times cell value minus 2 times cell value minus 3 equals to 0. If you plug in the 3 into the cell, this expression would be 0. But if you plug in this 100, then this expression would be like 99 times 98 times 97. It won't be 0, so this constraint is not satisfied. If you witness 0, this will not satisfy either. Next, let's convert in if and else. So for example, we have this sample program here. We have input A, input B, and there are like few elements. And happy is like a boolean value. And if happy, then we do A plus B. If we are not happy, we do A times B. And then so for example, we could have a circuit that looks like this. I use the glue to glue the public instance value to the private value. In the left one, we can see that witness A is 5 and B is 6, and happy is 1. So we are doing the addition path. So we got value 11. And the second one, we still have same AB value, but happy is 0. And the design output is 30. We just gave an example here. We haven't constrained them. The way you turn this if else into the gate expression would be happy times A plus B plus 1 minus happy times A multiplied B minus output equals to 0. So that if you're happy is 1, then this expression will be enabled, and this expression will be disabled. The other way is same. If the happy is 0, then this expression is enabled, and this expression is disabled. So basically, we have an example of value witness in here. These are all satisfied, but we kind of forget something here. Like Pruber can witness 3 here, and still got 3 times 5 plus 6 and get the output minus 27 equals to 0. So what's wrong here? It's because that every input is a finite field element, but if we want boolean value here, we need another constraint to make it boolean. So we need this additional constraint here to limit the happy to be 1 or 0. So using the tricks before. Next, we need to convert the loops into the circuit. Let's start with the easy one. This function initiate a variable R as 0, and then it runs the loop five times and add 5 to R and do it five times. And these are our constants. So it will be easy to lay out the value of R, start it with 0, and then plus 5, plus 5, plus 5, all the way to the output. And then we constrain the value 0 to 0, 25 to the output to the output. So we will have this gate expression like this. Easy. But what about this? This looks like exactly the same loop. It said how many times of loops is determined by the prover input here. So this will be a lot trickier because you might have a. So we can imagine that the patient trace looks like this. First, prover inputs the n here and the output 25 here, or input 3 here and input here. And notice that this algorithm, this is basically the very inefficient way to do five times n. And also note that this n could be arbitrary large, but we don't have an arbitrary large circuit. So we cannot do infinite loops. We can only do only after a certain amount of computation. So we need to restrict this. How big this n could be? Let's say we restrict it to five. And then we need another check to make sure the n is actually less than five, but it's out of scope of this talk. We want to focus on how to determine the computation of r inside. So let's first do the copy gate for 0 to 0 and the output to output. But because the copy gate output can only locate it at the same place of the cell, so if the n is free, we need to repeat the result here. All right, so one attempt is to solve this is we add a prover input selector here. So let's put 1, 1, 1 here when we are still in a range of n. And then 0, 0, 0 here to do the repeat. And using the if-else trick we learned before, we can define this gate expression. But then how do you prevent prover doing this? If prover, they don't follow the rules, they win a 0 and 1 and then go back to 0 and 1. And if you do the math, you can realize that prover can win a 3 and output to be 10. Then it convince you that 5 times 3 is 10. Then it's a failure. That people say is that blockchain is all about trust. I would say blockchain is all about trust issues. So let's observe different cases of n. Like n is 0, n is 3, n is 5. And we observe that s can only, for the valid case, you can start with 0. But once you are 0, you can only go with 0 of the rest of the row. You can start with 1 and you can turn 1 to 0 any time. But once you turn to 0, there is no way to turn back. So we can identify the state transition like this. So this is a trick Han taught me. So when you start, you can start at the 8th state. That means we need to add 5. And from 8th state, you can go back to 8th state. You can go to the past state, which is a 0, and the r remains the same. But the point here is that once you enter the past state, there is no way to go back to the s state. So we can define the constraint here. And actually, the only thing we need is here. Like if the current cell s is 0, for example, here, then the next cell must be 0. So this constraint can help you achieve that. And also, we need to make sure the selector is binary. Now we have a final touch. Like to make the input accumulate this input value and copy it back to input. This still follows the same rules from the proverbial witness selector. So why are we doing all these tricks? Why we need to do this repeating and state transition thing? Because in CPU, we take instruction one of the time. And if the branch you didn't enter, you can forget it and you don't compute it. But for a circuit, you need to flatten all the computation. All paths, you need to include it in your circuit. And then so you can see here, even though we only witness inputs free here, and these three rules, we still need to need them. And we need some dummy values there. Because we need to consider the case that proverb could witness five here and use all these spaces. The takeaway here is that the challenge for a circuit developer is that we are working with the computation trace instead of the execution itself. So we need to flatten the path and work all possible paths of your computation. The second is that because we are working with field element instead of bits and bytes, so we need to do some math and equations there. And the third is that we need to work with a verification mindset because proverb could be witness malicious values inside your circuits. And the tricks we talk about here are like using a boolean value and one times the true path and the other times the false path. This can convert the evals statement into the circuit expression. And for the complicated rules, you need to identify the state transition of your program logic and then design constraints for them. Yeah, that's my talk. Thank you. Thank you for listening. Do I have time? I thought that in Planck, the copy constraints were expensive. Can you expand on why they are cheap in Hello 2? I think they are the same. I think that they are cheap in terms of when you use once and you use it million times, they are the same cost. So I think that's the idea. Thank you. Great presentation. For somebody that wants to learn more about CK proofs, what path do you recommend to go? Right now, I think the circuit explanation is really good. But I got lost in some bits. So what will you recommend me to read in order to follow you better? I would recommend trying to find a simple circuit project and try to run it, try to tweak it, like removing something and try to study it. I think that would be a good way to learn how a circuit works. You mentioned the key points that you have to prevent the prover from cheating. How often do you see, when you're riding in circuits, that how often do you find bugs because your circuit was under constraint and the prover can cheat? Oh, good question. So the question is that how do we find if there are bugs? Or how often does it happen that you find a bug because it's under constraint and the prover can cheat? Or because if the constraints are wrong, the gates are wrong, or just trying to compare it to usual software development, when you just basically write the logic wrong. How often do you see that the gates that you come up with are doing the wrong thing? So when we are developing the circuit, oftentimes we'll realize, oh, we forgot this constraint and prover could witness the bad values. We found a lot of bugs in development stage. But once you want to go to production, we would do some external audits and try to find as much bugs as possible. And when you really enter the production, you can only cross your fingers. But I heard Vitalik has an idea that they can use two provers for the roll-ups. And if they can create a valid proof for two different outputs, then you can stop the roll-up or stop your application and then do the fix. So when you write the Halo 2 circuits, you're writing and using the Rust lib, right? Do you think that that adds a lot of overhead compared to if you were using a DSL, like Sercom or Zocredes, for example? I definitely feel like Sercom or DSL are more readable, more auditable. But the thing is, when you don't have DSL, you just have to work with it. So you would like to have a DSL for Halo 2? I like what? Would you like to have a DSL for Halo 2? Yeah, yeah, yeah, that would be every desire and great to have a DSL for Halo 2. I thought someone released one some months ago, didn't they? Or maybe I dreamed that. I don't know, anyone up here know? I think it was someone from Darkfight that posted on the ZK Podcast Telegram group. Oh, it sounds great. But maybe I also dreamed that, I don't know. Thank you.