From 447efee96884cd9e0796a2fc60b52865d674424d Mon Sep 17 00:00:00 2001 From: Giacomo Pasini Date: Wed, 26 Feb 2025 16:30:25 +0100 Subject: [PATCH] add initial sketch of swapvm stf --- emmarin/apps/swapvm/Cargo.toml | 2 + emmarin/apps/swapvm/app/Cargo.toml | 2 + emmarin/apps/swapvm/app/src/lib.rs | 76 +++++++++++++------ emmarin/apps/swapvm/stf/Cargo.toml | 3 + .../apps/swapvm/stf/methods/guest/src/main.rs | 47 +++++++++++- 5 files changed, 106 insertions(+), 24 deletions(-) diff --git a/emmarin/apps/swapvm/Cargo.toml b/emmarin/apps/swapvm/Cargo.toml index 4ce9087..567f95f 100644 --- a/emmarin/apps/swapvm/Cargo.toml +++ b/emmarin/apps/swapvm/Cargo.toml @@ -1,3 +1,5 @@ [workspace] resolver = "2" members = ["app"] + + diff --git a/emmarin/apps/swapvm/app/Cargo.toml b/emmarin/apps/swapvm/app/Cargo.toml index 211d3b2..b976ef4 100644 --- a/emmarin/apps/swapvm/app/Cargo.toml +++ b/emmarin/apps/swapvm/app/Cargo.toml @@ -4,3 +4,5 @@ version = "0.1.0" edition = "2021" [dependencies] +cl = { path = "../../../cl/cl" } +risc0-zkvm = "1.2" \ No newline at end of file diff --git a/emmarin/apps/swapvm/app/src/lib.rs b/emmarin/apps/swapvm/app/src/lib.rs index 80ac19d..a0b022c 100644 --- a/emmarin/apps/swapvm/app/src/lib.rs +++ b/emmarin/apps/swapvm/app/src/lib.rs @@ -1,4 +1,11 @@ -use cl::crust::{balance::UnitWitness, Nullifier, Tx, Unit}; +use cl::{ + crust::{ + balance::{UnitWitness, NOP_COVENANT}, + Nullifier, Tx, Unit, + }, + mantle::{ledger::Ledger, ZoneId, ZoneState}, +}; +use risc0_zkvm::sha::rust_crypto::{Digest, Sha256}; use std::collections::{BTreeMap, BTreeSet}; const SWAP_GOAL_UNIT: UnitWitness = UnitWitness { @@ -7,6 +14,12 @@ const SWAP_GOAL_UNIT: UnitWitness = UnitWitness { burning_covenant: NOP_COVENANT, }; +pub struct SwapVmPrivate { + pub old: ZoneState, + pub new_ledger: Ledger, + pub data: ZoneData, +} + pub struct Swap { pair: Pair, t0_in: u64, @@ -49,32 +62,23 @@ pub enum ZoneOp { Swap { tx: Tx, swap: Swap, - proof: DataProof, + proof: OutputDataProof, }, AddLiquidity { tx: Tx, add_liquidity: AddLiquidity, - proof: DataProof, + proof: OutputDataProof, }, RemoveLiquidity { tx: Tx, remove_liquidity: RemoveLiquidity, - proof: DataProof, - }, - UpdatePool { - tx: Tx, - pool: Pool, - proof: DataProof, + proof: OutputDataProof, }, Ledger(Tx), } // Txs are of the following form: impl ZoneData { - pub fn add_liquidity(&mut self) {} - - pub fn remove_liquidity(&mut self) {} - pub fn swap(&mut self, swap: &Swap) { assert!(self.check_swap(swap)); let pool = self.pools.get_mut(&swap.pair).unwrap(); @@ -83,7 +87,7 @@ impl ZoneData { } pub fn check_swap(&self, swap: &Swap) -> bool { - let pool = self.pools.get(&swap.pair) else { + let Some(pool) = self.pools.get(&swap.pair) else { return false; }; @@ -94,15 +98,19 @@ impl ZoneData { (balance_0_final * 1000 - 3 * swap.t0_in as u128) * (balance_0_final * 1000 - 3 * swap.t1_in as u128) - == balance_0_start * balance_1_start; + == balance_0_start * balance_1_start } /// Check no pool notes are used in this tx pub fn validate_no_pools(&self, tx: &Tx) -> bool { - tx.inputs.iter().all(|input| !self.nfs.contains(&input.nf)) + tx.updates + .iter() + .filter(|u| u.zone_id == self.zone_id) + .flat_map(|u| u.inputs.iter()) + .all(|nf| !self.nfs.contains(nf)) } - pub fn validate_op(&self, op: ZoneOp) -> bool { + pub fn validate_op(&self, op: &ZoneOp) -> bool { match op { ZoneOp::Swap { tx, swap, proof } => { self.check_swap(&swap) && self.validate_no_pools(&tx) @@ -117,7 +125,17 @@ impl ZoneData { } } - pub fn process_op(&mut self, op: ZoneOp) { + pub fn expected_pool_balances(&self) -> BTreeMap { + let mut expected_pool_balances = BTreeMap::new(); + for (Pair { t0, t1 }, pool) in self.pools.iter() { + *expected_pool_balances.entry(*t0).or_insert(0) += pool.balance_0; + *expected_pool_balances.entry(*t1).or_insert(0) += pool.balance_1; + } + + expected_pool_balances + } + + pub fn process_op(&mut self, op: &ZoneOp) { match op { ZoneOp::Swap { tx, swap, proof } => { self.swap(&swap); @@ -128,23 +146,35 @@ impl ZoneData { add_liquidity, proof, } => { - self.add_liquidity(&add_liquidity); + todo!() } ZoneOp::RemoveLiquidity { tx, remove_liquidity, proof, } => { - self.remove_liquidity(&remove_liquidity); + todo!() } ZoneOp::Ledger(tx) => { // Just a ledger tx that does not directly interact with the zone, // just validate it's not using pool notes self.validate_no_pools(tx); } - ZoneOp::UpdatePool { tx, pool, proof } => { - todo!() - } } } + + pub fn commit(&self) -> [u8; 32] { + let mut hasher = Sha256::new(); + for nf in &self.nfs { + hasher.update(nf); + } + for (pair, pool) in self.pools.iter() { + hasher.update(&pair.t0); + hasher.update(&pair.t1); + hasher.update(&pool.balance_0.to_le_bytes()); + hasher.update(&pool.balance_1.to_le_bytes()); + } + hasher.update(&self.zone_id); + hasher.finalize().into() + } } diff --git a/emmarin/apps/swapvm/stf/Cargo.toml b/emmarin/apps/swapvm/stf/Cargo.toml index 63b3854..09087b4 100644 --- a/emmarin/apps/swapvm/stf/Cargo.toml +++ b/emmarin/apps/swapvm/stf/Cargo.toml @@ -2,6 +2,9 @@ resolver = "2" members = ["host", "methods"] +[dependencies] +app = { path = "../app" } + # Always optimize; building and running the guest takes much longer without optimization. [profile.dev] opt-level = 3 diff --git a/emmarin/apps/swapvm/stf/methods/guest/src/main.rs b/emmarin/apps/swapvm/stf/methods/guest/src/main.rs index f60f5d2..3c7e289 100644 --- a/emmarin/apps/swapvm/stf/methods/guest/src/main.rs +++ b/emmarin/apps/swapvm/stf/methods/guest/src/main.rs @@ -1,5 +1,50 @@ use risc0_zkvm::guest::env; fn main() { - + let mut inputs: SwapVmPrivate = env::read(); + + let zone_id = inputs.zone_data.zone_id; + + assert_eq!(inputs.zone_data.commit(), inputs.old.zone_data); + + for op in ops { + zone_data.process_op(tx); + } + + let txs = ops + .iter() + .map(|op| match op { + ZoneOp::Swap { tx, swap, proof } => tx, + ZoneOp::AddLiquidity { tx, .. } => tx, + ZoneOp::RemoveLiquidity { tx, .. } => tx, + ZoneOp::Ledger(tx) => tx, + }) + .collect(); + + let sync_logs = vec![]; // get this from outside + + let outputs = txs + .iter() + .flat_map(|tx| tx.outputs.clone()) + .filter(|o| o.zone_id == zone_id) + .collect(); + let inputs = txs + .iter() + .flat_map(|tx| tx.inputs.clone()) + .filter(|i| i.zone_id == zone_id) + .collect(); + + let ledger_public = LedgerProofPublic { + old_ledger: inputs.old.ledger, + ledger: inputs.new_ledger, + id: zone_id, + sync_log, + outputs, + }; + + env::verify( + ledger_validity_proof::LEDGER_ID, + &serde::to_vec(&ledger_public).unwrap(), + ) + .unwrap(); }