#Why do many projects use the ZkProgram version of smart contracts

28 messages · Page 1 of 1 (latest)

storm island
#

Most of the examples and tutorials at the website instruct to create smart contracts by extending SmartContract class. However, many of codes I see used in reality extend ZkProgram. Their syntax is quite different.

Is the difference mostly about state and interoperability? So if you only want to generate proofs and don't care about state storage or interoperability it's easier to use a ZkProgram?

Why is it also so difficult to even find ZkProgram in the docs?

foggy swift
#

you can implement smart contracts as zk programs too, basically smart contract has some extra utilities over zk program to plug into the protocol itself, e.g. handling AUs in the background for you, state APIs too

#

at Protokit, we use ZkProgram quite extensively, any runtime module you write is a zk program!

storm island
#

hmm, interesting. So why bother with using ZkProgram at all, if SmartContract is more expressive?

foggy swift
#

because smart contract is specific to the L1 protocol, if you want to do general purpose proving, where you ‘dont touch’ the L1 protocol, you dont need the tools provided in SmartContract

storm island
#

right, so if you don't need the blockchain/network but just need proving, then ZkProgram might be better

#

can SmartContract be used outside the network?

#

or can ZkProgram be deployed to the network?

foggy swift
#

answer to both is yes, but there’s no gain in doing so

remote holly
#

ZkProgram also gives you ability to recursively prove the circuit. Proving recursively the smart contract circuit is technically possible, but much less convenient.

storm island
#

would be great to read some article about the differences

#

is there something in the docs?

#

I find it confusing

foggy swift
#

i’d suggest browsing the o1js codebase, esp how smart contracts are implemented, what the state and @method API does

verbal wolf
#

Ohhh this is an interesting topic!

verbal wolf
glossy bay
#

@storm island the most simple difference is zk program does not interact with Mina blockchain at all. Smart contract must exist on Mina blockchain.

The reason so many people use zk program is that no one likes working with the Mina blockchain L1. Don't let that deter you, as much more friendly paradigms are on the way, like protokit.

edit: others are saying that SCs can be used off chain, so that might be true. But, my point is still true in a practical sense. People associate SCs with on-chain and ZKPrograms with off-chain.

mental zinc
#

@storm island here are a bit more technical details:

Both ZkProgram and SmartContract are wrappers around Pickles, and they both reflect the Pickles API: Give me a list of circuits (methods), and I give your back a list of provers for those methods, plus a single verification key that can verify proofs of all the provers.

ZkProgram is actually a very direct translation of the Pickles API to TypeScript. With some usability enhancements like simpler caching. (And a much more usable type interface :P)

Pickles/ZkProgram allows you to define the public inputs and outputs of your methods. You might have noticed that all methods in one program need to have the same public input/output shape, which makes sense because another program which verifies proofs of this program needs to know the input/ouput shape statically.

#

SmartContracts are Pickles instances that all have to be recursively verifiable by a single circuit, the zkApp transaction circuit (which lives on the Mina node). In particular, all SmartContracts must have the same public input/output shape. That's one of a couple of things which SmartContract abstracts away: It defines the input/output shape for you (here: https://github.com/o1-labs/o1js/blob/bc945a0a4662bee66cde54fe47ef2bbe74185703/src/lib/zkapp.ts#L683-L685).

The SmartContract public input is actually two field elements: One is a hash of the account update of the SmartContract itself, and the other is a hash of all its child account updates. And SmartContract adds a little wrapper circuit to your method which performs this hashing, so that from within the contract you can just define the account update and its children directly and don't worry about hashing them / connecting them to the public input.

This is where SmartContract is inherently domain-specific: it is all about writing an account update and its children. If you don't care about the account update, there's no reason to use it over ZkProgram.

Apart from the public input wrapper, what SmartContract adds is mostly a DSL-like API to create the account update and its children from within the method. APIs like this.state.set() , this.state.requireEquals(), this.reducer.dispatch() etc, under the hood are all just mutating various fields on the account update.

One which is quite involved is the "calling other contracts" API (otherContract.someMethod()). Under the hood, it creates a child account update for the otherContract and adds a little circuit which proves that the someMethod() input/output arguments are consistent with what happens in the otherContract proof. Calling other contracts would be pretty hard without that DSL, so in a sense, SmartContract enables the feature of composability.

I hope that gives you an idea!

junior sparrow
storm island
#

thanks all for the explanations.
I guess the main idea is that contracts are on-chain and programs off-chain, although in theory they could be interchanged.
Yes, I'd very much like to see this in the docs. Or even better (but a lot more thought-provoking): since they're so close to each other, why bother having two different things anyway - maybe remove programs and add just a flag or two for contracts (if even that is needed).

mental zinc
# storm island thanks all for the explanations. I guess the main idea is that contracts are on-...

why bother having two different things anyway - maybe remove programs

Interesting suggestion. I'm against it, because I think we need a non-Mina specific, general purpose way of defining zk circuits and creating proofs. SmartContract isn't that because it fixes the public input, and adds extra (expensive) stuff to every single circuit.

Also, I actually prefer the ZkProgram API. So what I'd much rather do is leave ZkProgram as is and make SmartContract a natural extension of it, for example a function (like ZkProgram) which internally calls ZkProgram.

What do others think about this though? Do you prefer the SmartContract API (class-based, decorators) or the ZkProgram API (function, more low-level, but because of that, much greater expressivity with types -- i.e., proving is just calling your method)

storm island
#

hmm, true. an extension would maybe make more sense. but I think the worst case is that there are two separate APIs if they're mostly used for very similar stuff.
Or at the very least, more documentation needed.

verbal wolf
#

@storm island Just out of curiosity, based on Gregor's explanation or what you're looking to build, are you leaning towards using the ZkProgram or SmartContract APIs at the moment? Curious to know what you're working on!

storm island
remote holly
mental zinc
# remote holly What would be the actual difference and how would the zkapp state variables look...

State variables could be declared as part of the config object along with methods and their arguments. The this instance which acts as the API for constructing the account update could instead be the first argument (replacing the public input argument in ZkProgram methods). Something like

method(self, ...args) {
  let x = self.x.getAndRequireEquals();
  self.x.set(x.add(1));

  self.account.isNew.requireEquals(true);
  self.reducer.dispatch(...);
  // etc
}