o1-labs / o1js

TypeScript framework for zk-SNARKs and zkApps
https://docs.minaprotocol.com/en/zkapps/how-to-write-a-zkapp
Apache License 2.0
475 stars 105 forks source link

Experimental offchain storage pt 1 #1630

Closed mitschabaude closed 1 month ago

mitschabaude commented 1 month ago

MVP design of an offchain state implementation.

Design

There are currently two types of offchain state: OffchainState.Field (a single field of state) and OffchainState.Map (a key-value map).

To use offchain state, a smart contract developer must

Some design decisions:

Caveats / Future work

While writing the example, I realized that the pure set() API, which instructs offchain settlement to blindly overwrite a current value with the new one, is a major footgun and probably only useful for specific scenarios (e.g. each user of a contract is empowered to manage their own slice of state and overwrite it without taking into account old state. example: registration as a voter), but fails as a solution for the majority of use cases.

Q: Should we rename set() to overwrite() to be more honest about its limitations?

An extension that can be made easily is to add an update() method which not only takes the new state, but also the old state to replace. It only succeeds if the old state is correct, i.e. it acts exactly the same as a precondition for onchain state.

With update(), what we arrive at is a clean extension of the onchain state API to offchain state of arbitrary size. It completely solves the size problem. However, it doesn't fully solve the concurrency problem:

One idea to improve shared state handling is to introduce user-defined (or predefined) commutative actions. For example, an action could say "add this number to the state" instead of "change the state to this value".

The final design should probably be more similar to protokit's runtime modules: A client/server like model where arbitrary computation is run in an offchain reducer to go from the previous to the next set of states, in one atomic update.