add initial sketch of swapvm stf

This commit is contained in:
Giacomo Pasini 2025-02-26 16:30:25 +01:00
parent ec65ece77b
commit 447efee968
No known key found for this signature in database
GPG Key ID: FC08489D2D895D4B
5 changed files with 106 additions and 24 deletions

View File

@ -1,3 +1,5 @@
[workspace]
resolver = "2"
members = ["app"]

View File

@ -4,3 +4,5 @@ version = "0.1.0"
edition = "2021"
[dependencies]
cl = { path = "../../../cl/cl" }
risc0-zkvm = "1.2"

View File

@ -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<Unit, u64> {
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()
}
}

View File

@ -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

View File

@ -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();
}