# ZK PoC - Use [RISC ZERO](https://www.risczero.com/) as our zkVM. - [PoC Repo](https://github.com/bingyanglin/risc0-rust-examples/tree/ledger-poc) ## Current Progress - [Flowchart](https://docs.google.com/presentation/d/1fMzQblrBzzYUwTzkHkTeqecNmkcRLXSPA73igDy0ix8/edit?usp=sharing) - Already implemented the client/server-side APIs and programs which can - Rollup the transactions into a bundle - The operator can request the L2 (RISC0 VM) to rollup the transactions and generate a validity proof - Then the rolled-up transactions can be included in a new transaction and gossiped to other nodes - Note: The operator maintains the ledger state, which contains the transferring amount of each account of the users - The nodes (participants) can verify the validity proof to unlock the the new transaction - What is lacking now - To integrating this item with an existing node - Create a tx which contains rolled up information (a batch of txs), and based on the ZK validity to unlock it - Integrating the PoC - Options of Codebase for the integration - iota.go - goshimmer - multiverse - The difficulty of the integration - iota.go - Need to hardcode the PoC into the stardust VM - Will have lots of technical debts due to the hardcoding - Will not use the results of the PoC in the future if we are going with L1 SC - goshimmer - hive.go (used by goshimmer) is under refactory and many conflicts (also might contain bugs hard to find/fix in a short time) - Goshimmer will be deprecated soon and a new repo will be created - THe unlock and alias logic is not touched for a long time and might be buggy - multiverse - Lacks of the unlock logic and alias logic - Don't have a concrete transaction structure for integrating our PoC ## RISC-0 Basics - [Understanding the prover](https://www.risczero.com/docs/examples/starter) - `Receipt`: Validity Proof (Journal + Seal) - The journal contains the public outputs of the computation. - The seal is an opaque cryptographic blob that attests to the integrity of the computation. It consists of merkle commitments and query data for an AIR-FRI STARK that includes a PLONK-based permutation argument. - `Prover`: Take the ELF file and the ID - ELF: the binary file for `method` execution - Each method is used to generate the `commit` - ID: the hash of the ELF file ## Mockup - Users can send a batch of transactions to this L2 zkVM with provided credentials. This zkVM will then generate a validity proof and send back to the user. The user then can directly use the proof to unlock the batch of transactions if the proof is verified valid. ## Specification - A ledger records tokens transferring between addresses - The ledger contains addresses (a map) - key: address - value: token - A ledger maintainer maintains the ledger, and is able to issue valid Transactions - The transaction contains receiver/sender addresses and tokens to be transferred) - A valid issuing operation will mutate the ledger state ### core/src folder - lib.rs (Define structures) - The commit contain the `state`, whose type is `risc0_zkp::core::sha::Digest` ```rust pub struct LedgerState { pub addresses: BTreeMap<String, u32>, pub transfer_count: u32 } impl LedgerState { pub fn transfer(&mut self, receiver: &String, sender: &String, tokens: u32) -> bool{ // Transfer tokens from a sender address to a receiver address // Return whether the transfer is successful or not } } pub struct InitializeLedgerCommit { pub state: Digest, } pub struct Transaction { pub receiver: String, pub sender: String, pub tokens: u32, } pub struct IssueTransactionCommit { pub old_state: Digest, pub new_state: Digest, pub receiver: String, pub sender: String, pub tokens: u32, pub transfer_counted: bool, } pub struct IssueTransactionParams { pub state: LedgerState, pub transaction: Transaction, } pub struct IssueTransactionResult { pub state: LedgerState, pub transfer_counted: bool, pub tokens: u32, } impl IssueTransactionParams { pub fn new(state: LedgerState, transaction: Transaction) -> Self { // Create a IssueTransactionParams } pub fn process(&self) -> IssueTransactionResult { // Transfer tokens from the sender address to the receiver address // Return the IssueTransactionResult } } ``` ### method folder - guest/src/bin folder - init.rs - Generate the `InitializeLedgerCommit` by the RISC0 VM - issue.rs - Generate the `IssueTransactionCommit` by the RISC0 VM - src folder - build.rs - `risc0_build::embed_methods()` ### host/src folder - Contains the structures and methods for creating and verifying the receipt) - lib.rs - `InitMessage` (contains Receipt) - `pub fn get_state(&self) -> Result<InitializeLedgerCommit>` - Get the state (receipt.journal) - `pub fn verify_and_get_commit(&self) -> Result<InitializeLedgerCommit>` - Verify the receipt by calling receipt.verify(), and return the commit - `IssueTransactionMessage` is similar to `InitMessage` - `LedgerMaintainer` (Generate the receipt by running the Prover) - `pub fn init(&self) -> Result<InitMessage>` - `pub fn issue(&mut self, ballot: &Transaction) -> Result<IssueTransactionMessage>` - Testing - Create a new LedgerMaintainer - Prepare Transactions - Issue Transactions via the LedgerMaintainer, and get the IssueTransactionMessage - Verity IssueTransactionMessage - Check the transaction commits and the transfer_count ## How to run this PoC - [Install Rust](https://www.rust-lang.org/tools/install) - Run the test ``` cargo test --release ``` - Run the client/server program - Use two terminals - Go to the server folder - `cargo run --release` - Go to the client folder - `cargo run --release` ## Console Results ```ssh running 1 test [2023-02-02T10:49:18Z INFO zk_poc] init [2023-02-02T10:49:25Z INFO zk_poc] issue: Transaction { receiver: "addressB", sender: "addressA", tokens: 100 } [2023-02-02T10:49:33Z INFO zk_poc] [0, 0, 0, 100, 4219768674, 3848250171, 2773697214, 2696326980, 2029397789, 624371590, 231552048, 2447920911, 4219768674, 3848250171, 2773697214, 2696326980, 2029397789, 624371590, 231552048, 2447920911, 8, 1919181921, 1114862437, 8, 1919181921, 1098085221, 100, 0] [2023-02-02T10:49:33Z INFO zk_poc] Ok(IssueTransactionResult { state: LedgerState { addresses: {}, transfer_count: 0 }, transfer_counted: false, tokens: 100 }) [2023-02-02T10:49:33Z INFO zk_poc] issue: Transaction { receiver: "addressC", sender: "addressB", tokens: 50 } [2023-02-02T10:49:40Z INFO zk_poc] [0, 0, 0, 50, 4219768674, 3848250171, 2773697214, 2696326980, 2029397789, 624371590, 231552048, 2447920911, 4219768674, 3848250171, 2773697214, 2696326980, 2029397789, 624371590, 231552048, 2447920911, 8, 1919181921, 1131639653, 8, 1919181921, 1114862437, 50, 0] [2023-02-02T10:49:40Z INFO zk_poc] Ok(IssueTransactionResult { state: LedgerState { addresses: {}, transfer_count: 0 }, transfer_counted: false, tokens: 50 }) [2023-02-02T10:49:40Z INFO zk_poc::tests] transfer count 0 [2023-02-02T10:49:40Z INFO zk_poc::tests] initial commit: Ok(InitializeLedgerCommit { state: 628f84fb3ba35fe5be4253a544afb6a01d27f678862737253034cd0d0f4fe891 }) [2023-02-02T10:49:40Z INFO zk_poc::tests] transaction 1: Transaction { receiver: "addressB", sender: "addressA", tokens: 100 } [2023-02-02T10:49:40Z INFO zk_poc::tests] transaction 1 commit: Ok(IssueTransactionCommit { old_state: 628f84fb3ba35fe5be4253a544afb6a01d27f678862737253034cd0d0f4fe891, new_state: 628f84fb3ba35fe5be4253a544afb6a01d27f678862737253034cd0d0f4fe891, receiver: "addressB", sender: "addressA", tokens: 100, transfer_counted: false }) [2023-02-02T10:49:40Z INFO zk_poc::tests] transaction 2: Transaction { receiver: "addressC", sender: "addressB", tokens: 50 } [2023-02-02T10:49:40Z INFO zk_poc::tests] transaction 2 commit: Ok(IssueTransactionCommit { old_state: 628f84fb3ba35fe5be4253a544afb6a01d27f678862737253034cd0d0f4fe891, new_state: 628f84fb3ba35fe5be4253a544afb6a01d27f678862737253034cd0d0f4fe891, receiver: "addressC", sender: "addressB", tokens: 50, transfer_counted: false }) test tests::protocol ... ok ```