ZK Voting App Deep Dive: Mixers, Merkel Trees, and Build with Noir - BuidlGuidl's Builder Bootcamp

BuidlGuidl's Builder Bootcamp at Devconnect Argentina 2025 · 18 Nov 2025 · 59:02

Philip Krause walks through how to build a fully private ZK voting application, starting with how a mixer similar to Tornado Cash breaks the link between a voter's address and their vote. He explains why a lean incremental Merkle tree suits on-chain registration, then shows how to define the zero-knowledge circuit in Noir, use nullifiers to stop double voting and generate a Solidity verifier contract. The session is part of BuidlGuidl's Builder Bootcamp at Devconnect Argentina 2025.

Speakers

Topics

From BuidlGuidl's Builder Bootcamp at Devconnect Argentina 2025, hosted by BuidlGuidl.

Transcript

Oh, and how does the screen sharing work? Ah,  perfect. Hi, welcome to our next challenge or our next topic we want to speak to. It's about  CK. But before we going to um start with that, I just want to chill quickly our speedrunners  event on Thursday night. Um we have kind of organized a bar with some free beer, some free  drinks and would be happy if some of you will going to join. So you have also the chance to  talk the build guild crew, the build guild team and yeah met us all in person. I think that's  our first time that we're doing something like that. So it would be very nice if you would come.  Here's kind of our QR code. So if you want to if if you want to scan it, then you will find a  link to our Luma event and sign up for that.

Okay. But here how do you I don't know how this work. Okay. But it's okay.  Okay. Let's start with the secret. So I'm Philip from the build guild. I'm um happy to have all  here. I had the honor of the last couple of weeks to go a little bit deeper into the Zika route  and see what kind of apps we can build with it. I mean we have all these mathematics about CK very  complicated stuff but what actually you could do with it and yeah and that's how the the CK voting  app um um came up with and the next hour I just want to walk you through the CK voting app all  the basic concept how it is done and then in the

end leave you out of the room that you can build  your own app that will be my my my goal for today but first things first who of you have built a CK  app so are one person. Okay. Not so many. So it's per two persons. Nice. So the the work workshop  is perfect for you. And what about who of you have kind of voted in some onchain proposal or  some voting or whatever. Yeah. Quite quite a ton. And this is kind of what happened also to me  because the thing is when you vote on chain and anyone kind of knows your address all the votes  you're doing are just out there in the public and everybody knows on which proposal you you voted  at and that exactly why we why we why we're here today. So kind of the the presentation is kind of  split. I walk you through some things. First we're

going to touch some how mixers work. That's kind  of like tornado cache and how how these things are done. Then we need to touch on very important  concept. Maybe for you it's sometimes it's it's Merkel trees but it's a special version of a  Merkel tree. It's a lean incremental Merkel tree. Then we going into the CK circuits with Noah.  What sounds very fancy and very complicated but actually it isn't. So it's pretty cool to see that  and how we can get a very nice solidity verifier very easy out of that and then in the end all  kind of how we putting everything together. But so first let's go with the the the mixer part.  And the thing is is everything is kind of on a on a blockchain is transparent. I mean that's very  good in in some sense because it's it's just very transparent and it's just no trust needed. Huh?  Because you can see where your funds go, how many

funds are in a protocol, in a smart contract. This  is just open to the public and so nobody really can scam scam you because you know exactly where  the money flows. Of course, they can take it away, but you see at which address then has the money.  So, it's completely different than in the banking system. But the issue is also for you. Imagine  it's me. I going to get paid um some salary on a address, Ethereum address that I own and my  employee employer already knows that address. So, they sends the fund to that and what I'm going  to do now. So, these people in the company know my address and if I even send it to another  address, the employee can just look into EA scan, see the transfer and see it. Okay, the account  the new account. So I really cannot all my all my

wealth everything I have onchain is kind of public  as soon as anyone knows the address. So they see all transactions, they see kind of the balances of  all ESC20 tokens, just everything everything open and I think I mean that's an issue u especially  for me and also all kind of other things you do on chain. It's not only sending money, you can  also call functions in some smart contracts, you're playing games, whatever. Everything is  just open. And that's kind of a huge issue. And so the the main issue really is how do you kind  of break this connection between one address? It's kind of the the sender address and the receiver  address. And in this case there there's one way and that's kind of mixers. So that's kind of a  like a mixer in a basic concept. So you have on the one side you have kind of many people. they  come all together and they and they kind deposit

something or put a secret into kind of a mixer.  So all in the pool together and then in the end it happens that's kind of the last step. They  withdraw or going to do something out of the pool. But the thing is everything in the mixer  gets mixed. So there's no connection anymore from the one group who started to the other group. You  only know okay that were the people but you don't know what each individual in there has done. This  is kind of the the the concept of a of a mixer. And and like like I said, it has three phases.  So the first phase is the deposit phase. And here um we we we kind of have a user that's you that's  me kind of we going to generate some kind of secret. And this is only known to us. So we keep  this secret. This is like our we should never head

it out. It's a little bit like our private key  for for for any wallet. And um this secret kind of gets hashed into a commitment. So this could  this commitment gets onchain and I could pair it in this sense with some ETH together and these go  into into the mixer and the secret stays with me. So I can at some point prove because I have the  secret I can hash it and I can say okay I have the commitment to that one and in the second phase  many more users doing that and they all putting their EE into one pot. So actually there's just a  huge pond where everyone throws in their their Eve and in the end everybody thinks okay that's my Eve  and that's my Eve and that's my Eve but in the end nobody knows whose E is what. So so that's kind  of the whole thing and the only thing what you

have you have kind of a note somehow you created  before and this note can then prove that you are an owner of one of these Eve but e all these Eve  in there are kind of the same. So how so how how is it done? I'm the user. I using a complete  different account. So it's important that it's not my account address because if I would use  again my same account address everybody knows that's me. So using a different address I somehow  have a secret with the secret I proof in the in in in in the background to the smart contract that I  can pass the gate in case this dog and I can take my Eve out. That's kind of the basic concept of a  mixer. And here I gave the example with some Eve,

but you could also use the gate instead of the E  withdraw. You can call whatever function could sit here in the back. What whatever you want. And in  this case for our voting app, it's the it's a vote function, but we will see that later in a little  bit. So that's kind of the the basic idea of how how a mixer works. One important thing about  it because I was talking about if you imagine you have this whole group here here on the left  side of course if if we would imagine in the in the in the in the in the extreme case there's  only one person using this protocol I mean this whole privacy wouldn't make sense because there  would be here one person put something in what should we get mixed there is only one Eve in there  and I take it out of course everybody would know

that's me so it's very important the bigger you're  getting this size on this side the better is your privacy because imagine If you have two people,  you still need okay maybe these two people but if it's 10 people it's thousand people it's 100,000  people the privacy just kind of grows and now an important thing here to know is we came with the  example of one fixed domination one E here I put the example if you would go with a some weird  number I going to put in there uh 378 E and in the end to later point I will withdraw 3.78 E  it's kind of very obvious who's this person is hu everyone else puts one Eve and I'm one person  who puts that Eve in and then everybody would know okay the new address that's exactly that address  to put it in and you have immediately a link again

so the connection is not really broken and that  why you have in these protocols I mean there are some some ways you have a fixed domination so  if you look for instance for some privacy pools or tornado cache they have one pool with one Eve  another pool with five E and then the other Eve a pool with 10 Eve and that's kind of is it done  I mean there are the other things I don't want to get any advances right now. You can also use some  UTXO and then you split it up and then you cannot mix it anymore. Again, that's like newer things,  but this is the the core thing and actually to say the bigger the people are, you have a  fixed domination, the better it is in general. That's kind of the the base for our CK voting app.  We're using a mixer, but instead of withdrawing

some Eve, we're going to call a function in here.  And therefore um I going to split it into here in into in two sides. So we have kind of the back  end side I just call the back end on this side and we have kind of the front end side on the  back end side. This is everything has to do with uh solidity and no on this side and getting our  solidity verifier contract out because we need that to plug it in. It's kind of this dog you can  imagine we we have seen. And then on the other side we have the front end. Normally in the other  speedrun challenges we always focus on solidity but here it's a very important part the front end  because we need to generate the proof of chain on our machine because this is all secret it's  kind of the private we don't want to leave that

out because that's us so we have to touch in this  challenge also kind of the front end and generate there the proof so we can later then plug it in  and send it to the to to to the verifier contract. Okay, first things first, we need to do the  registration. That's kind of the first phase we saw in this in in in this mix of things. So  somehow we need to put our commitment somewhere that we can later prove that we are part of this  whole group. That's what we're going to do first and therefore we're going to use a Merkel tree.  I don't know. I mean Merkel tree in the first place sounds very easy. If you go for me, I mean  it's it's very basic. If you go deeper in it, it gets more complicated. And then if you think  about other things like I don't know it gets very

complicated. So I I I kind of tried to break it  a little bit further down. So I don't know all who of you is familiar with Merkel trees and how  they work. Yeah a couple of people. So I just just just for the other ones I just going to summarize  it and try to do it quickly before we going to go then to the incremental Merkel trees and how this  is different to a normal Merkel tree. Okay. Here on the one side we have a normal normal Merkel  tree. we have kind of four values in that. So that's kind of the registration. So you could  say okay we have four users reg registered and the first user has a secret one two three four  that's kind of the values could be hashed things and this merkele tree has a depth of two now  because we have we we we always count from zero

so it's kind of a depth of two so we have three  levels 0 one two that's kind of the thing so when we want to have a new party registering to that  Merkel tree because we need to somehow track the the the data structure who has registered um what  will happen to this normal Merkel tree is it gets blur probably very big much bigger because we need  to add another node that would be the five but we cannot plug it in here because we we we're looking  at that we're going to create the whole right side of the Merkel tree again so this one then to  calculate we have to go calculate we have to increase the depth this is kind of complicated  and put everything on chain I mean a normal Merkel tree for instance is good when you when you  consider Ethereum block you have an Ethereum block

you're getting all the transactions in and then  there is the final state of the block and then you after transaction nothing changes anymore it gets  proposed and the whole mercury is calculated but we won't change that that merkele tree anymore  but here and and then it can be there and then and then it's good so for so so for fear blocks  is it's very nice to have just a merkel tree but um but to add more um to to more things  to register our people that doesn't work because we have over and over recalculate the  whole Merkel tree and that's kind of very very very very cumbersome and just not the best way to  do and that's There are in there are incremental Merkel trees and the difference is that we go in  and we say okay we want to have a certain amount of leaves that's all these numbers and we're  going to fix the tree already. So it's already

there. So as you can see we have here eight  leaves that would le would mean we have place for eight users that could use our protocol  kind of and but these leaves here are already empty like before they're already there and what  we're going to do we're going to preopulate them all with zeros or whatever we want to have for  instance for tornado it's tornado the the word tornado it's hashed and it's put in here and now  if you're going to put one uh one new value in here we only have to recalculate the the kind  of the frontier is it I'm going to explain in a second what that exactly is. And the the cool  thing about this one, you kind of can track the the frontier more or less and put the whole  thing on chain and you always know where you at.

So in this incremental Merkel tree here, you  can see now we're going to add the five. So we're going to put the five in. We hash it  together with the zero and we just go up. And this is kind of the front where I meant.  So if we then would put populate this one, we only we do the we do the cell  calculation, but the tree itself always stays the same. So it need it needs  to kind of um prefix from the beginning. So and I was talking about the I mean that that  that that's important thing about the frontier how how it gets calculated so that we can save  the store on chain and this is exactly how it happens here. So because we say okay we have um a  depth of three in the other one in the mer tree we saw before just go back quickly here we have uh  up up to level three. So that's kind of a depth

um has three four levels more or less that's why  we starting here and we're having this little array here in in in this corner and now how how  it happens. So we're going to add the number one we're going to get the I don't know we're looking  at the array and say okay the slot is empty so we can just put it there. So we have the number  one stored. This is how when the first user adds it to the adds himself to the Merkel tree  or the commitment registers for the application, it's here. So the second one adds the two. Now  it comes now now it gets a little bit complicated because we already have in the first position we  have the number one. What we're going to do, we're going to use the number one hash it together with  the number two. We clear the spot the the slot and

carry it forward to the next one. So we have it  stored. Now only with this little array we know exactly how the merkel trick uh tree looks like  and where we have to add the next next value to it and so on. Number three, it's very easy because  we have the zero. We just put it here. The four, it gets then a little bit more complicated because  um we have the the three is kind of um occupied. The fif the the the zero slot is occupied. The  first slot is occupied. So what we're going to do, we take the four, we hash it with the three.  We need to go to the next slot. The next slot is also full here. So we hash these two together  and we're going to move it forward to this one.

And that's then how our frontier like this this  kind of this border around would look like. And we know exactly only storing this array how our  merkel tree looks like. And you could imagine when we when we would extend that to a depth of  16 or whatever, we would have already I don't know 60 70,000 something in between of leaves  in there when we have the fixed depth. But then you just have an array with 16 numbers. So it's  pretty cool actually to track the the state of the of the merkelene. That's what we're using for  registering the people. This is very gas efficient and it also works very good with CK. That's why  we're using this incremental mer tree in there.

Yeah, maybe. Okay. I I'm I'm kind of  switching now from the presentation to the code to show you actually how how  our codebase is set up and how we work in the different circuits and how we going  to implement it in the solidity code. So let's go into cursor. I have already installed  everything and the the the thing is I have already put in all the solutions in there. So the code is  full full. If you would do the challenge yourself, you you find an empty spots like like you maybe  know it from other challenges in there. So what you can see here we have another another  package. Is it big enough to see? No. No. It's better.

Okay. I mean that's that's kind  of the normal normal scaffold Eve um um setup. The the only thing you can see we  have another folder in here that's a circuits here. Here will everything happened with the  with the NAR in a little bit. Then we have here the hardhead and then we have the next chairs.  Our contract looks pretty easy because I mean probably many of you have just a very counter  app voting app. It's just pretty pretty basic go going forward. So if you're going into  the voting app we close all this stuff. I mean we have to we have the normal normal uh  the voting app as you can see we're using the lean IMT from here so a library is integrated so  it's very easy we don't we we don't write this oursel we're going to use that from the from  the CKit that's a very nice library out there

we're using that one so then we can initialize  more or less the the tree here as a empty empty empty variable in the beginning and um regarding  the functions so we have kind of how should I say for for for the voting we doesn't want to vote  let vote everyone because otherwise we could just create random addresses let everyone vote and we  had kind of a civil attack our voting attack we want to make sure that there are only dedicated  people can vote I mean there there are many ways and the easiest one is just have a allow list  just send all of my colleagues just send me their addresses when and I add them to the allow list  and then know these addresses can only vote on this question for instance. This the thing that's  why we have the add voters here and um then here

we have the register contract uh function sorry  for that and this is kind of a normal non normal register like like we do and the thing is um as  you can see we have embedded here the tree and we're going to inserting the comments that get in  from the from the register function and this nice library tracks it. So you're going to add that and  everything is kind of calculated already and they will do the the frontier calculation for you. So  it's in there and we keeping with that track of the whole registration and which are the users  who have registered already and of course then we're going to emit a leave and we're going to um  and and we also going to set um the commitments um to true if it's the case and and they and  they have registered. That's kind of the thing.

I mean that's pretty straightforward to do  that after you have understood how kind of this incremental thing works because I think it's  it's pretty important to build the whole thing. So when we have do that kind of the registration  effect now comes the fun now we going to we we we going to jump into no and that's kind of the  circuits. So okay maybe maybe in the beginning I mean there a couple of words I mean I I just took  them out. I mean the one thing is kind of an zero knowledge proof. Probably everyone of knows of  you knows already what it is. It's kind of you have kind of a secret and you are kind you are the  prover with the secret or whatever you know and you want to prove to another person that's kind  of the verifier that you know the secret without

without revealing the secret. That's exactly what  we need to do there. So we know we we we have the secret we have the commitment and we we have the  secret we don't want to reveal it but we have to make somehow sure that the verifier believes that  one commitment in the whole mer tree belongs to us that's kind of the most important thing and that's  exactly where why we use a zero knowledge proof for that and then we have I just pointed out four  words they always appear somewhere and they're a little bit confusing to me because I I didn't know  that's why I just I mean a circuit what what is a circuit it always sounds so fancy in the end it's  just a program you right kind of in Noah and that program you use then to create a verify contract  or whatever. It's kind of the the the the set of

rules you define something with a proof needs to  pass. Then we have the constraints or the assert function that's actually in the contract. It's  like um assert just a normal assert function if everybody knows you in the in the in the circuit  itself in the program. Then we have the witness and the witness is kind of the you could have  many inputs in going into a proof for instance and using these proofs the these inputs to create  the witness. This kind of is a secret to you but with this witness you can you can kind of convince  the the back end to create the proof that you know these inputs and and out of that comes the proof.  So you can imagine you're going to build the the circuit then you're going to get the the witness  out of it and with the witness then you can have

the proof out of that but we will we're going  to see it in a in in a in a little bit. Yeah. And that's kind of the most important the terms  I would say for general understanding what's going on. Okay. And the next thing I want to talk  quickly about is no. I mean I have mentioned it. I don't know how how many of you know it already.  that's kind of a a a programming language kind of adopted from Rust. So it has a Rust base more or  less. It's developed by the AdSec labs. They're also coming up with this little privacy layer and  so on. And and and this one you can use to create um all kind of um circuits yourself and write them  in a very easy way because it's just a rust base and not something other fancy. And with that there  comes Nago. I don't know that's kind of the the

cargo like the the CLI tool for Rust but just for  na for for Noah that's why it's called Nago and we have always this Barrettenburg I don't know in the  beginning I was a little bit confused what is this Barretenburg is the Barrettenburg more or less  is the kind of the back end who who runs all the difficulties in the back end to create the proof  and so on that's what that's what it does so you can imagine the the Noah writes you the circuits  and you're going to pass in the circuits with the inputs to the Bretonbeck they do some magic fancy  mathematic stuff out and and the proof comes out. Yeah. And maybe because I mentioned it, let's  jump into into how a NR um circuit looks like.

So I have a set it up and this is kind of the it's  a little bit difficult. We will go through step by step to each of the different things the lines why  they're in there. But it's more or less like you can see it looks like Rust, doesn't it? It's just  like the normal Rust thing. And um what is the important thing here when you when you're creating  these circuits, you're having two different types of inputs. So you have public inputs and you have  private inputs. The public inputs are visible to everyone. So everyone sees what are the public  inputs and you even need to pass that in into the verifier to prove the proof. But there are  also these private inputs and these are kind of the secrets or the things you doesn't want to let  know anyone. And as you can see here or it has no

color really here all these pop these pop words  means it's it's it's public and if they have no like these two here they are kind of private and  here comes already the third the the first thing I kind of want to talk to is kind of our secret  because we have the secret together. So we have an um hashed of two values. It's one time the  nullifier and it's the secret to make it a little bit because we want to make sure that  um only one commitment can go onto the onto the um onto the Merkel tree. That's why we're going  to track the nullifier as well and we're going to store this nullifier also then in our contract so  that no not many people can just over and over and over again vote in the whole thing or create or  in there. That's why we're going to we're going to

store it. And let me go back to the presentation a  little bit. So that's kind of maybe the commitment scheme we having. I mean this is pretty that's  pretty straightforward. We have the secret, we have the nullify, we hash it. That's a  commitment and that ends up in here. And like I said before, the secret and the nullify  shouldn't leave shouldn't leave your laptop. Another thing with the nullifier is we also  have to kind of we have the nullifier hash. So we don't use the nullifier itself. We want  to use the nullifier hash. And maybe it gets a little bit too complicated. I hope I don't don't  confuse you all. Maybe I don't know you can give me a quick sign. Is it too fast? Is it too slow?  Is it all good? Okay, cool. Because the important thing and that took me some time to wrap my head  around and hopefully I get it right again is

um you need to track the nullifier somehow like  like I said because you don't you want to prevent the double spending stuff but you also don't  want to have because it's a secret you don't want to have it out there. So you going to use  the nullifier hash and inside the circuit what you want to check you want to always check against the  nullifier hash so that a user because you can you can you can imagine you're using your nullifier  you're going into the proof um a valid commitment is is kind of computed and you're passing the  proof and everything in there. But if you don't check against the nullifier, you can do the same  again, but you just choose a completely random nullifier that is not there and the proof just  passes. That's why you want to compute inside the um inside the circuit your nullifier and compare  it with the public nullifier hash that comes into

here. Does it make sense? Probably was a little  bit complicated. You can ask me again afterwards. Does it make sense? Okay, let me go in here. So  we have a we we have a nullifier hash in here. This is public because you have this in the in the  in the solidity contract to track which nullifier gots already um um voted on. So you want to you  want to you want to track that. But the nullifier is kind of a secret. You want to you want to  have it secret away. That's why here here you see it's private this one. So what we're going to  do the nullifier um the the nullifier but you can see kind of the nullifier hashes you can you can  kind of you can kind of see because they are there

they're public but you want to make sure that no  one um can many more times call the same function. That's why you're going to use the nullifier.  You hash it in here so the the person needs to pass in the nullifier but it's private and you  cannot guess you cannot guess a nullifier. And then you compare the computed nullifier hash with  the nullifier hash you have passed in. Okay, good. Yeah, this is one this is this is what one thing  to keep in mind in here. And I mean it's also pretty easy to calculate a hash. Maybe I have to  mention that there are there very nice libraries in here you can just use and you can use from from  the Poseidon. This is a hash function. This is

kind of catch for Ethereum. you see many times,  but this is kind of made for for for CK. So, it's just more efficient and better to use. And  you can have these libraries and then you're calling in these these function more or less.  Yeah, that's the one thing you have to keep in mind. Let me let me jump this a little bit. I  mean, here you can um because you're going to pass in if you go back in here in the function,  we're going to pass in the siblings. The siblings are kind of the nodes in the Merkel tree. So  you always have the siblings but these these siblings need to be private because as soon these  gets public you know exactly where you are in the Merkel tree and you don't want to give up your  position because then everybody knows who puts the

commitment in because you signed the transaction  before put the commitment somewhere but you don't want to you want to do that. I have to keep the  siblings always private and to calculate the depth it's like we have seen in the front you can and  that you can you can kind of um count you take out all the the zeros because it's a fixed array  we have here a fixed array of 16 you're taking out the zeros and then you're having the the the  the length of the array and then you know exactly the depth of the of the mer tree and this one I  going to do here I I let compute there has some some advantages and then we pass it later in if  you go here into the computed root thing that's what we kind of do in here and then there is a  little bit weird thing in here because um we need

to let the the function here this is the burner  burner Merkel um root function that comes from the from a library I import similar to the solidity  um um library but this is for made for Noah and um here we need to let it know which exact index  we are at. And what we if you go up to the inputs we see we have um the index is a field. A field  is kind of a integer in in in that way. It's just the the very basic um unit you will put in and  whatever you don't know you put a field in first kind of. So that's a UN 256 in in in solidity more  or less. And there there's a very very nice trick and that you cast down the number into into kind  of bits. So you break it so so you break it down.

And how this works also here made um a little  a little explanation to that. So when we want to get the the index in binary what we have  we have the index. So let's see. Okay we have number four. That's kind of our fifth value you  put in there. It's a it's a number four because we start counting with a zero. That's a zero one  0 0. Then what we do, we're going to reverse the order and we pat everything um to reach the length  of 16 because we predefined that that's kind of our length of the our our predefined depth of our  Merkel tree in that sense. And then when we want compute now the root in here we're getting in the  siblings from here because we have zero and zero and 1 2 3 4 when we go here we have we be putting  the five. So one sibling is zero. This is the next

one. The other zero and from this the sibling is  1 2 3 4. And then we know the depth and we have the index we just we just recently calculated. And  then we have two rules to follow. If it's a zero, we had the the the hash looks like this. We have  it on the left side, the current what comes in and the sibling to the right side. And if it's  a one, we we we have it the opposite around. This is exactly what they say here. So when we go  for the zero, we're going to go over here, we say the current goes to the left, goes to the left,  and the sibling here. So we know we need to hash these because when you're hashing two values, the  order matters a lot. So if you change that, it's a completely different hash. So you always need  to make sure you really hash in the same order.

Yes, that will be kind of do. And when we go  now back here to the um to to to our circuit, you see in here that we kind of then asserting  I just I just going to sum it up a little bit because that will be take now probably a little  bit longer. Um here the the most important asset we do in here is the computed route um equals  the other route that we're going to do. We're going to because we're going to pass in the  root what is the latest route stored in our in in our solidity contract and compare it to  the to the normal route. If if this is correct then the mer tree is good and the merke proof  is good and then we're going to let it let it go. Otherwise we just reject it because it's just  [ __ ] what the person said because that doesn't

can calculate the tree all all the way up because  they don't have the right things. This is kind of here it works. And then there is maybe a um an  important thing to mention. As you can see when we go up here I see the vote and it's kind of a  um um the the the question if it's a true or a no you want to vote on. So it's kind of a binary  question with a bool we chose and and in here we need to have it in here because if the and  bind it to the proof because if we wouldn't do that we wouldn't have it in there then someone  can just jump on the blockchain on just frontr run us and just change the vote that would be  possible but the thing is since we bind the answer of the vote what we want to vote into the  in into the proof nobody afterwards can change it.

Yes, that's kind of the the circuit how how it  looked like. Maybe if somebody has a question afterwards um we can discuss it or you can come  to me. I'm I'm the whole day here and we can just discuss. I'm also happy to learn more about  the these things. But it's definitely very cool. And now we coming to the coolest part, what I  really enjoyed the most and that is generating the solidity verifier contract because this is  just magic for me and that's pretty cool and I think that makes it very easy to use these  things because you saw it's just this little rust written program you have in the end you're  getting a verifier contract you deploy and you can just integrate into your in into your  smart contract. Maybe before we doing this I just wanted to give you a a a quick heads  up. So, um, if you want to do it in the CLI,

we can we going to CD to our circuits folder  and and then we just going to run a nago check. Ah, maybe we need to do a nago compile  before because I deleted the folder. Okay, now we have it. Okay, what nago compiles does it compiles  the whole thing down and we have a JSON and um and all about this the circuit and you can  use it then for the for for for the front end to call all the libraries in there. This is this  is another thing but here when you run the nago check here you see all the um the inputs you have  to make in order to create your witness and then you prove in the end and this is exactly kind  of as you as you saw before our in public and

private inputs in here and when you fill these  in I mean I don't don't do this now because it's just getting complicated with the siblings  because you have to create all the different hashes and you need to to to add it to it and  you need to pre-populate it up front and so on. But this is kind of what you would do in a  CLI form. So to in order to try out the circuit how it works and then you can run against that  you can create a witness and then out of it in the CLI you can create a proof and then you  have a just a big a big proof of of a lot of numbers and you could use it to do something  with it kind of that that would be in the CLI but the cool thing is we don't have to do that  in the CLI because we're going to do this in a

second in the front end where you're just calling  the the the right libraries from CKit for that. So what we would do now in order to create the um  the solidity contract is actually just these two commands you're going to run. So here we're going  to start we're going to use the the Barrenburg and um we we first kind of need to  create a verifier key is it called and this is kind of the base and then out of that they going to create the the the solidity  contract. But let's run this in a second.

Hopefully this works now. Yes. And  as you can see here over there, we we we we have the verifier key. That's it.  And now let's go call Barrenburg again. And we call the function write solidity uh verifier  and we're going to pass in the verifier key and where it should put the verify contract. And as  you can see, we have now a full verify contract very easily created in here. It's a very long  one. And this just gets created automatically in there. And there's only one important thing  in here. That's the verify function. So we have a nice interface here. But if you look at the  verify function, this is exactly what we need to call. And we can just use it as a condition in  our function. We want to call that in a second.

and one yeah let's maybe we we have the verify  contract now let's jump into our contract that's the voting dossole contract so the register  function I won't touch further on and then we here we have the voting function this kind of  the voting function and here you can see this line here is where we call the verify contract  and we're going to pass in the proof and we're going to pass in the public inputs. So the  public input was everything that has a pop keyword before that is that is open and the proof  is here and before that you can do that you have to of course define getting your public inputs  and in the right format this is always the the main or that could be some some issues how you  get it from the front end then into the solid

cont that all the formats formatting is right I  was struggling there a bit with it one very thing one very important thing and I forgot it first is  we want to check for the root. So in a solidity context whenever we going to add with the insert  function up here insert the root changes because it recalculates above the root and it gets  stored in the contract. And what we're going to do the the the person creating the proof  is calculating the proof kind of offchain and um and then asserts in inside the proof also  has to like like we saw we we calculate the roof and we we put the proof in. But we have we  need to make a connection to the proof in the smart contract because if you wouldn't do that I  could just come up with a complete random tree I

created offchain and just pass it in. It will work  but it's completely disconnected from the tree. So everyone my my my whole contract wouldn't work  anymore and everyone can just do whatever they want and just vote as as they want. And that's  a and that and that's a very important thing to to keep in mind. Yeah, I think I think that's  it. That's kind of the most important thing in here. I mean you're going to check here as well.  You're going to set the the the nullifier hash so that a person cannot vote again. So it's  only one time voting. So you want you want to track that. And then in the end you just  just count count the votes on on these sites.

So are there maybe I just going to make I looking  very confused faces. So I just want to give just wanna are there any questions the mid time in  the meantime? Anything that's unclear, confusing test. Yeah. So, can I get um  clarification on the nullifier hash? So, what's to stop a voter  from using a separate nullifier?

Sorry again. So what's so so you have the public  nullifier hash, right? And then you have the the voter has a private nullifier. So what's to  stop uh a voter from using another private nullifier? I mean every person who is registered  has with the commitment put on the Merkel tree and the registration consists of a secret and  a nullifier. So they unique. So as a person I'm register having only one nullifier. All the  other nullifiers won't work because I cannot come up with a random nullifier because  I don't have the commitment on the chain on the chain in the Merkel tree. Does it make  sense? Yes. I guess I can also later then when when you want come to me we can just go through  and I can show you the flow inside if you want.

Okay. So what we Oh, another question. Yes. So  my question will be initially testing. Yeah. I was trying to speak loud so you can everyone can  hear me. Um yes. So my question will be you said initially you need to send a list of address to  be what list in order to be included in the marker tree. Uh can be compute to figure out if they have  common root. My question if I send my address to that person to be white list they already know who  I am right so in that case you know realistic um voting scenario I don't want anyone know who I  am but I still want to vote do you know what I mean like I don't want like to give someone at  least there's a single authority in this case

they know I've been whitelist so how prevent being  reveal my identity to even that single authority to be included in the first place if that makes  sense to you. Um I'm not sure if I understood it correctly. You mean that um with the allow  what is kind of the benefit of the allow list? Yeah. Like I Exactly. Yeah. Can you like do you  think allow list in that sense? I mean you want to kind of create the voters. So imagine um a  a country election for instance. You only want to have your citizens vote. you don't want to have  anyone else vote because yeah I'm I'm from Germany so only German citizens are allowed to vote but  if you're from the US you're not allowed if you don't hold a German pass so you kind of restrict  the voters in this sense so you could have kind

of different requirements for the voters coming  in and the easiest one is you have an owner of the contract and that adds the addresses to the  allow list and these persons who are in the allow list can register themselves with registering  putting their commit commitments on the chain on in the Miracle tree. So does that mean in the  case of um citizenship verification the whole market trail including the whole German population  like in term like on the whole tree pre pre-white list in that sense yes they're pre-whit listed I  mean that goes a little bit further away that's kind of use case I thought also about it might  be happen that you kind of get sending to each of every German you would send a letter and there  is a private number in it and you use that private

number maybe as the registration to the contract  kind of but I mean there are there are different ways I mean I can go later you the different  things you could use to have people registered you can look for balances of ESC20 token or NFTTS  or whatever you want in there but the thing is as soon as you open it up to everyone everyone can  just vote creating addresses you don't get the effect anymore because you can just have civil  attack the whole thing so you need to have some restriction correct for for the application yeah  The reason I'm confused is like during the first step of verification preet listing process um  how can you award to be not identified that is the kind of the confusion I have so I'm more  about the single point of failure during their

initial verification of setting up their mercury  tree if that maybe I can show you let me See, I can walk you through the um if it loads. Okay, maybe. Okay, sorry for the maybe. Okay,  I see I don't have so much time anymore. Um the thing is um like I said the the I mean there  there's one time you're going to create the

commitment offchain. That's just a thing. I just  going to touch it briefly. As you see the secret and nullifier are important because anyone  who has these two can prove that they are on on the Merkel tree and then your whole privacy  is gone. So you keep that on your on your device and that's it and you're going to generate this  offchain and you only put then the hash version of these two onchain that's kind of the commit  commit scheme we having and then um there is a proof generation so the the the generating  commitment is for registration and then when you want to vote you need to generate a proof that  you're on the Merkel tree and you're and you're there and I just going to show quickly in the in  the nextjs to the component Oh no, where is it?

The gener the generate proof. I mean what we want to do we as you can see maybe  the first thing we we need to create the tree offchain. So we need what we need to to do on the  um for the for for the proof generation. We are just on our laptop and we need to reconstruct now  for ourselves because we know where we are and we have the inputs. we need to reconstruct the whole  tree and that's what we're going to do also with a with a lean IMT like a lean incremental merry  library in here and then we're going to form all the inputs and we're going to um because we have  them the the the private ones and then we're going to let no use no we're going to um we're going to  generate the witness and then we're going to pass

it in into the Brenenburg back end similar like  I said before and then we're going to generate the proof and that proof then we're going to pass  into the voting function the functionality that's kind of in a in a nutshell one one important point  I want to mention and sorry that I have to to to cut that short right now but it's just because the  time is the separation of the addresses because you need another address in order to call the vote  function when you have the proof because if you would use the exact same address again what is  the whole point I create any proof then we could just leave all this the the SEO knowledge  proof away. So, we need a way to have a a fresh address that is not linked to your address  because even like like I said in the beginning,

if you just have a new an address, there's  your funds. You're going to generate a new address and you're going to set funds to it.  Everyone who got can do a little bit of chain analysis would know this account also belongs  to you. So, you really need a complete fresh address to vote. And therefore, I mean, if you're  using local chain and yarn and the yarn chain, just it's it's very simple. You're just going  to spin it up and you just in a deploy strip, you say, "Okay, fund this account." But how does  this work in a in a real world application? And the thing is that you're just going to that you  use smart um smart account abstraction with a verifying pay master. So you have a pay master  somewhere, you're going to spin up completely fresh a fresh account and that gets then paid with  a verify pay master that belongs to the protocol

for instance. then you have no link to that and  you don't even need to know this account who voted then for you because you will never use it again.  Just one time created pay the transaction fee and then it goes in. This is also a very important  point when you think about considering building these application that you always for the new  address you need some way to sponsor the the transaction because you want to break this link  between the um sender and the receiver address. Maybe a a little things I just want to elaborate  on are kind of the final final considerations I had with the app. I mean I haven't touched  it. The thing is you want to have some kind of a root history array because imagine many  people interacting with your with with your

chain and they're going to add new commitments.  Whenever they add a new commitment or get a register there is a new mer there's a new merkel  route. But when you're only tracking this one, it happens many times. You generate the the proof  offchain. You're going to send it to the mercury. But in the meantime, someone else already added  the commitment to the mercury. That means a new merkel root would have been created. And so the  comparison fails even if you have the secret everything well done because just the merk root  changed. And that's why a good behavior is to have an array. So you're kind of having different the  the the all that you're collecting kind of I don't know it depends how much you want 10 or whatever  kind of these um the these different roots in an array and you're always letting drop out the  oldest one so to make to to to to make it work and

another thing what is also very important because  you need to build the whole Merkel tree yourself in the front end you need very good indexing  for that because you need to look to all the um to to to to keep the thing running. Um then as  I said really the privacy depends on the amount of people. So you're always making sure that  you're having as many people using that when you want to achieve huge privacy. Of course it  doesn't matter if you have a voting app like us and we're using it in the build guild and we have  50 people and 15 people and we just let them vote. We know that's all all the voters. But the more  precarious it is or the more you want to really get the privacy on some withdrawal or or whatever  then the the the the bigger amount is good. And

of course the gas sponsorship is another point  I would like to mention that what I said with the with the account abstraction. There's also a  thing you need to wrap around how you going to do that in order really to break break cleanly the  the separation. Then another big point I put it here that was kind of what we're touching on is  kind of the registration strategy because it's also a hassle. So imagine you want to I want  to use now a hidden vote um voting application with with you all and I only have the the the the  allow list where I add the addresses. What I need to do now first I need to collect each of you one  address and then add it to the allow list and then you can vote but this is a very cumbersome process  isn't it? So I mean there are there are other ways

we could have given out to all voters some NFTs  already. Then I check for the NFTs and that's could be the allow list or whatever you come up  with that that's another idea you can do and oh it turned and another limitation to the whole  application is and I mean Vitalik has written a blog post about it and there's a protocol out  there that's kind of the Macy protocol Macy framework or infrastructure and what it exactly  means because in this way we constructed this challenge you can prove pro in the end to anyone  what I have voted on because I have the proof. So if Austin asked me hey Philip I give you 10 Eve  when you tell me what you have voted on I would then give him the proof and say yeah I have voted  yes where are my 10 eaves kind of and the thing is

with the Macy with the Macy framework it kind of  um protects you because it's another third party integrated in the whole thing I mean that also  leads to more complicated and to more trusted setup kind of but this will will deal with the  whole telly process and in that sense because um because CK you cannot prove what you have voted  on. But on the other side it has also a downside because um they cannot fake your vote but they can  withhold your vote then in the end. So you kind of um you kind of introduce some censorship resistant  again in the whole thing. What you need or want.

So I think the real voting is not invented  yet. There's there's definitely plenty room to um play around with and try out CK because  I think it has so many possibilities. And if you're thinking of I just used now the vote  function in there but actually that's kind of the basis for everything in the voting and you  can just put whatever you want behind the behind behind the dog what people can do. Yeah, I think  that's it. I mean I that's it more or less what we have what we have touched we have touched the  commitments kind of how you how how using that to bring some secrets on the blockchain then um  how we going to build the no and the circuits a little bit I didn't go too much deep into that I  thought they had more time but there wasn't the

case merries why they're important and what  what is the difference between a normal one and the lean incremental merkel tree and then  the coolest thing how we getting the solidity verifiers out of it and we just can can use it  for our smart contract. Yeah. And that's it from my side. I hope it was not too confusing. I  think we still have a couple of minutes for any questions. If there are otherwise like I said  is I'm here the whole day. So if there is anyone wanted to discuss with me something, I'm happy  to do that because I really like the topic and I think has so much potential. It's pretty  cool. And yeah, that's it. And I don't and don't forget our speedrunners event. Here's here's  the Where's the Here's the Kuco to it. Thank you.

Oh, doesn't work. Okay, then  then sorry. Oh, I had Oh, there's a weird address in there. Can it  work? It's a Eve address. Ah, now there it is. I don't even know which address that was was now  it should work. Sorry for that. Does it work? U one question. Um I understand Solidity  smart contracts have a maximum size right and the contracts that Noir generates are pretty  big. So I was wondering if um this is something that limits the use cases of of CK technology and  also if there's a way to realize beforehand if if

your contract is going to be too big or just  going to realize when you compile it. Yeah, I have heard that in the beginning that it's  very big and I I mean I scrolled through the contract and you saw how long it is to verify  a contract but um we were deploying also we we built some voting app with the contract. It's a  little bit um bigger and but the circuit stay the same. It was no issue and not so expensive to to  put that on base. I haven't done it with mainet that probably could be a different story but on  base it was no issue deploy the smart contract. Thank you. Uh, hi Philip. I only want to add  something to his question. H maybe you can check gracias. Maybe you can check semaphore.  Semaphore allows you to hide the identity

of the people inside the voting group and you  can combine with this uh example. Semaphur. I can share you the link. And what semaphro does it  hashes the identity. You can use a sign a a firm from your wallet. You can use a secret word. You  can use a hash that you hold an specific NFT or a specific token and then use that. The tricky  part is how you design the mechanism to detect what kind of variables are you using into the  entry group. But yeah, it's from PSC. Thank you. Hello. Well, thank you very much for your talk  first of all. Then I don't know if my question

is uh kind of basic, but can you simply track  I don't know if you can see on real time how the vote in advance is if you see there's  a registration of course of a movement if an address votes for something and then  you see like the pool moving forward if you can somehow in that like link. Is it okay  speak a little bit louder? Yeah, sorry. So, isn't there a way in which I could link if I see a  transaction registering from an address that it's voting and then I see like the boat pool somehow  moving, isn't there like couldn't I simply like track or trace back to which is the address that  actually voted? Yeah, that's a very good point actually. And yeah, when we did we we we built a  kind of a more productive app. It's kind of hidden vote. we we we started to build out and there we  integrated a registration period. So before you

can vote I mean I haven't even shown now the  app um you have a registration period because exactly what you said you want to you don't want  to allow that someone registers and immediately vote because this causality you can just link  and then you know exactly that person might be exactly that voter. Yeah, that's why we if you  want to host a vote for instance, you can say, "Okay, the voting period, the registration period  is open for one week, everyone registers and then you you you open it up afterwards and then let  the people vote and then you don't have this issue anymore because you don't know who then  voted and which could relate to which address.

Okay, thank you very much. [Applause] [Music]