mirror of
https://github.com/logos-blockchain/logos-blockchain-pocs.git
synced 2026-01-07 23:53:11 +00:00
commit
19c66f31c0
@ -4,6 +4,7 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.8.5"
|
||||
cl = { path = "../../../cl/cl" }
|
||||
risc0-zkvm = "1.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
@ -1,23 +1,91 @@
|
||||
use cl::{
|
||||
crust::{
|
||||
balance::{UnitWitness, NOP_COVENANT},
|
||||
tx::LedgerUpdate,
|
||||
InputWitness, Nonce, Nullifier, NullifierCommitment, NullifierSecret, OutputWitness, Tx,
|
||||
Unit,
|
||||
},
|
||||
mantle::ZoneId,
|
||||
};
|
||||
use rand::RngCore;
|
||||
use risc0_zkvm::sha::rust_crypto::{Digest, Sha256};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
const FUNDS_SK: NullifierSecret = NullifierSecret([0; 16]);
|
||||
pub const ZONE_ID: [u8; 32] = [128; 32];
|
||||
|
||||
pub fn swap_goal_unit() -> UnitWitness {
|
||||
UnitWitness::nop(b"SWAP")
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct SwapOutput {
|
||||
// value will be set at the market price
|
||||
pub state: [u8; 32],
|
||||
pub unit: Unit,
|
||||
pub nonce: Nonce,
|
||||
pub zone_id: ZoneId,
|
||||
pub nf_pk: NullifierCommitment,
|
||||
}
|
||||
impl SwapOutput {
|
||||
pub fn basic(
|
||||
unit: Unit,
|
||||
zone_id: ZoneId,
|
||||
nf_pk: NullifierCommitment,
|
||||
rng: impl RngCore,
|
||||
) -> Self {
|
||||
Self {
|
||||
state: [0; 32],
|
||||
unit,
|
||||
nonce: Nonce::random(rng),
|
||||
zone_id,
|
||||
nf_pk,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SwapArgs {
|
||||
// the user specifies the template forthe output note
|
||||
pub output: SwapOutput,
|
||||
// minimum value of the output note
|
||||
pub limit: u64,
|
||||
// the nonce used in the swap goal note
|
||||
pub nonce: Nonce,
|
||||
}
|
||||
|
||||
impl SwapArgs {
|
||||
pub fn to_output(self, value: u64) -> OutputWitness {
|
||||
assert!(value >= self.limit);
|
||||
OutputWitness {
|
||||
state: self.output.state,
|
||||
value,
|
||||
unit: self.output.unit,
|
||||
nonce: self.output.nonce,
|
||||
zone_id: self.output.zone_id,
|
||||
nf_pk: self.output.nf_pk,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn swap_goal_note(nonce: Nonce) -> InputWitness {
|
||||
InputWitness {
|
||||
state: [0u8; 32],
|
||||
value: 1,
|
||||
unit_witness: swap_goal_unit(),
|
||||
nonce,
|
||||
zone_id: ZONE_ID,
|
||||
nf_sk: NullifierSecret::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: order pair tokens lexicographically
|
||||
fn get_pair_share_unit(pair: Pair) -> UnitWitness {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(b"SWAP_PAIR_SHARE_UNIT");
|
||||
hasher.update(&pair.t0);
|
||||
hasher.update(&pair.t1);
|
||||
hasher.update(pair.t0);
|
||||
hasher.update(pair.t1);
|
||||
UnitWitness {
|
||||
spending_covenant: NOP_COVENANT,
|
||||
minting_covenant: NOP_COVENANT,
|
||||
@ -35,63 +103,12 @@ pub struct Swap {
|
||||
t1_out: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AddLiquidity {
|
||||
pub pair: Pair,
|
||||
pub t0_in: u64,
|
||||
pub t1_in: u64,
|
||||
pub pk_out: NullifierCommitment,
|
||||
pub nonce: Nonce,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SharesToMint {
|
||||
pub amount: u64,
|
||||
pub unit: Unit,
|
||||
pub pk_out: NullifierCommitment,
|
||||
pub nonce: Nonce,
|
||||
}
|
||||
|
||||
impl SharesToMint {
|
||||
pub fn to_output(&self, zone_id: ZoneId) -> OutputWitness {
|
||||
OutputWitness {
|
||||
state: [0; 32],
|
||||
value: self.amount,
|
||||
unit: self.unit,
|
||||
nonce: self.nonce,
|
||||
zone_id,
|
||||
nf_pk: self.pk_out,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RemoveLiquidity {
|
||||
shares: InputWitness,
|
||||
nf_pk: NullifierCommitment,
|
||||
nonce: Nonce,
|
||||
}
|
||||
|
||||
impl RemoveLiquidity {
|
||||
pub fn to_output(&self, value: u64, unit: Unit, zone_id: ZoneId) -> OutputWitness {
|
||||
OutputWitness {
|
||||
state: [0; 32],
|
||||
value,
|
||||
unit,
|
||||
nonce: self.nonce,
|
||||
zone_id,
|
||||
nf_pk: self.nf_pk,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ZoneData {
|
||||
pub nfs: BTreeSet<Nullifier>,
|
||||
pub pools: BTreeMap<Pair, Pool>,
|
||||
pub zone_id: ZoneId,
|
||||
pub shares_to_mint: Vec<SharesToMint>,
|
||||
pub shares_to_redeem: Vec<OutputWitness>,
|
||||
pub swaps_output: Vec<OutputWitness>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)]
|
||||
@ -100,6 +117,15 @@ pub struct Pair {
|
||||
pub t1: Unit,
|
||||
}
|
||||
|
||||
impl Pair {
|
||||
pub fn new(t_a: Unit, t_b: Unit) -> Self {
|
||||
Self {
|
||||
t0: std::cmp::min(t_a, t_b),
|
||||
t1: std::cmp::max(t_a, t_b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Pool {
|
||||
pub balance_0: u64,
|
||||
@ -108,102 +134,87 @@ pub struct Pool {
|
||||
pub total_shares: u64,
|
||||
}
|
||||
|
||||
/// Prove the data was part of the tx output
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct OutputDataProof;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ZoneOp {
|
||||
Swap(Swap),
|
||||
AddLiquidity {
|
||||
tx: Tx,
|
||||
add_liquidity: AddLiquidity,
|
||||
proof: OutputDataProof,
|
||||
},
|
||||
RemoveLiquidity {
|
||||
tx: Tx,
|
||||
remove_liquidity: RemoveLiquidity,
|
||||
proof: OutputDataProof,
|
||||
},
|
||||
Ledger(Tx),
|
||||
}
|
||||
|
||||
/// This contains the changes at the ledger level that can only be done by the executor
|
||||
/// because they are dependant on the order of transactions.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct StateUpdate {
|
||||
pub tx: Tx,
|
||||
/// Update the balance of the pool
|
||||
pub pool_notes: Vec<InputWitness>,
|
||||
impl Pool {
|
||||
pub fn price(&self) -> f64 {
|
||||
self.balance_1 as f64 / self.balance_0 as f64
|
||||
}
|
||||
}
|
||||
|
||||
// Txs are of the following form:
|
||||
impl ZoneData {
|
||||
/// A swap does not need to directly modify the pool balances, but the executor
|
||||
/// should make sure that required funds are provided.
|
||||
pub fn swap(&mut self, swap: &Swap) {
|
||||
assert!(self.check_swap(swap));
|
||||
let pool = self.pools.get_mut(&swap.pair).unwrap();
|
||||
pool.balance_0 += swap.t0_in - swap.t0_out;
|
||||
pool.balance_1 += swap.t1_in - swap.t1_out;
|
||||
}
|
||||
|
||||
pub fn check_swap(&self, swap: &Swap) -> bool {
|
||||
let Some(pool) = self.pools.get(&swap.pair) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let balance_0_start = pool.balance_0 as u128;
|
||||
let balance_1_start = pool.balance_1 as u128;
|
||||
let balance_0_final = balance_0_start + swap.t0_in as u128 - swap.t0_out as u128;
|
||||
let balance_1_final = balance_1_start + swap.t1_in as u128 - swap.t1_out as u128;
|
||||
|
||||
(balance_0_final * 1000 - 3 * swap.t0_in as u128)
|
||||
* (balance_1_final * 1000 - 3 * swap.t1_in as u128)
|
||||
== 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.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 {
|
||||
match op {
|
||||
ZoneOp::Swap(swap) => self.check_swap(&swap),
|
||||
ZoneOp::AddLiquidity { tx, .. } => self.validate_no_pools(&tx),
|
||||
ZoneOp::RemoveLiquidity { tx, .. } => self.validate_no_pools(&tx), // should we check shares exist?
|
||||
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)
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
nfs: Default::default(),
|
||||
pools: Default::default(),
|
||||
zone_id: ZONE_ID,
|
||||
swaps_output: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pair_price(&self, t_in: Unit, t_out: Unit) -> Option<f64> {
|
||||
let pair = Pair::new(t_in, t_out);
|
||||
self.pools.get(&pair).map(|pool| pool.price()).map(|price| {
|
||||
if t_in == pair.t0 {
|
||||
price
|
||||
} else {
|
||||
1.0 / price
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn amount_out(&self, t_in: Unit, t_out: Unit, amount_in: u64) -> Option<u64> {
|
||||
let pair = Pair::new(t_in, t_out);
|
||||
let pool = self.pools.get(&pair)?;
|
||||
|
||||
let (balance_in, balance_out) = if pair.t0 == t_in {
|
||||
(pool.balance_0, pool.balance_1)
|
||||
} else {
|
||||
(pool.balance_1, pool.balance_0)
|
||||
};
|
||||
|
||||
let amount_in_after_fee = amount_in * 1000 - amount_in * 3;
|
||||
|
||||
let amount_out =
|
||||
(balance_out * 1000 * amount_in_after_fee) / (balance_in * 1000 + amount_in_after_fee);
|
||||
|
||||
Some(amount_out / 1000)
|
||||
}
|
||||
|
||||
/// A swap does not need to directly modify the pool balances, but the executor
|
||||
/// should make sure that required funds are provided.
|
||||
pub fn swap(&mut self, t_in: Unit, amount_in: u64, swap: SwapArgs) {
|
||||
// TODO: calculate amout outside proof and check here for efficiency
|
||||
let amount_out = self.amount_out(t_in, swap.output.unit, amount_in).unwrap();
|
||||
|
||||
let pair = Pair::new(t_in, swap.output.unit);
|
||||
let pool = self.pools.get_mut(&pair).unwrap();
|
||||
|
||||
let (balance_in, balance_out) = if pair.t0 == t_in {
|
||||
(&mut pool.balance_0, &mut pool.balance_1)
|
||||
} else {
|
||||
(&mut pool.balance_1, &mut pool.balance_0)
|
||||
};
|
||||
|
||||
*balance_in += amount_in;
|
||||
*balance_out -= amount_out;
|
||||
self.swaps_output.push(swap.to_output(amount_out));
|
||||
}
|
||||
|
||||
/// Check no pool notes are used in this tx
|
||||
pub fn validate_no_pools(&self, zone_update: &LedgerUpdate) -> bool {
|
||||
self.nfs.iter().all(|nf| !zone_update.has_input(nf))
|
||||
}
|
||||
|
||||
pub fn pools_update(&mut self, tx: &Tx, pool_notes: &[InputWitness]) {
|
||||
let Some(zone_update) = tx.updates.get(&self.zone_id) else {
|
||||
// The tx is not involving this zone, nothing to do.
|
||||
return;
|
||||
};
|
||||
// check all previous nullifiers are used
|
||||
assert!(self.nfs.iter().all(|nf| tx
|
||||
.updates
|
||||
.iter()
|
||||
.filter(|u| u.zone_id == self.zone_id)
|
||||
.flat_map(|u| u.inputs.iter())
|
||||
.find(|nf2| *nf2 == nf)
|
||||
.is_some()));
|
||||
assert!(self.nfs.iter().all(|nf| zone_update.has_input(nf)));
|
||||
self.nfs.clear();
|
||||
|
||||
// check the exepected pool balances are reflected in the tx outputs
|
||||
let outputs = tx
|
||||
.updates
|
||||
.iter()
|
||||
.filter(|u| u.zone_id == self.zone_id)
|
||||
.flat_map(|u| u.outputs.iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let expected_pool_balances = self.expected_pool_balances();
|
||||
for note in pool_notes {
|
||||
assert_eq!(note.nf_sk, FUNDS_SK);
|
||||
@ -211,40 +222,12 @@ impl ZoneData {
|
||||
let output = note.to_output();
|
||||
let value = expected_pool_balances.get(&output.unit).unwrap();
|
||||
assert_eq!(note.value, *value);
|
||||
assert!(outputs.contains(&&output.note_commitment()));
|
||||
|
||||
assert!(zone_update.has_output(&output.note_commitment()));
|
||||
self.nfs.insert(note.nullifier());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_minted_shares(&self, tx: &Tx) {
|
||||
let outputs = tx
|
||||
.updates
|
||||
.iter()
|
||||
.filter(|u| u.zone_id == self.zone_id)
|
||||
.flat_map(|u| u.outputs.iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for shares in &self.shares_to_mint {
|
||||
let output = shares.to_output(self.zone_id);
|
||||
assert!(outputs.contains(&output.note_commitment()));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_redeemed_shares(&self, tx: &Tx) {
|
||||
let outputs = tx
|
||||
.updates
|
||||
.iter()
|
||||
.filter(|u| u.zone_id == self.zone_id)
|
||||
.flat_map(|u| u.outputs.iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for shares in &self.shares_to_redeem {
|
||||
assert!(outputs.contains(&shares.note_commitment()));
|
||||
}
|
||||
|
||||
// TODO: chech shares have been burned
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -255,78 +238,35 @@ impl ZoneData {
|
||||
expected_pool_balances
|
||||
}
|
||||
|
||||
pub fn add_liquidity(&mut self, add_liquidity: &AddLiquidity) {
|
||||
let pool = self.pools.entry(add_liquidity.pair).or_insert(Pool {
|
||||
balance_0: add_liquidity.t0_in,
|
||||
balance_1: add_liquidity.t1_in,
|
||||
shares_unit: get_pair_share_unit(add_liquidity.pair).unit(),
|
||||
total_shares: 1,
|
||||
// TODO: only for testing purposes
|
||||
pub fn add_liquidity(&mut self, t0_unit: Unit, t1_unit: Unit, t0_in: u64, t1_in: u64) {
|
||||
let pair = Pair::new(t0_unit, t1_unit);
|
||||
let pool = self.pools.entry(pair).or_insert(Pool {
|
||||
balance_0: 0,
|
||||
balance_1: 0,
|
||||
shares_unit: get_pair_share_unit(pair).unit(),
|
||||
total_shares: 0,
|
||||
});
|
||||
let minted_shares = (add_liquidity.t0_in * pool.total_shares / pool.balance_0)
|
||||
.min(add_liquidity.t1_in * pool.total_shares / pool.balance_1);
|
||||
pool.total_shares += minted_shares; // fix for first deposit
|
||||
pool.balance_0 += add_liquidity.t0_in;
|
||||
pool.balance_1 += add_liquidity.t1_in;
|
||||
let (balance_0, balance_1) = if pair.t0 == t0_unit {
|
||||
(&mut pool.balance_0, &mut pool.balance_1)
|
||||
} else {
|
||||
(&mut pool.balance_1, &mut pool.balance_0)
|
||||
};
|
||||
|
||||
self.shares_to_mint.push(SharesToMint {
|
||||
amount: minted_shares,
|
||||
unit: pool.shares_unit,
|
||||
pk_out: add_liquidity.pk_out,
|
||||
nonce: add_liquidity.nonce,
|
||||
});
|
||||
*balance_0 += t0_in;
|
||||
*balance_1 += t1_in;
|
||||
}
|
||||
|
||||
pub fn remove_liquidity(&mut self, remove_liquidity: &RemoveLiquidity) {
|
||||
let shares = remove_liquidity.shares;
|
||||
let (pair, pool) = self
|
||||
.pools
|
||||
.iter_mut()
|
||||
.find(|(_, pool)| pool.shares_unit == shares.unit_witness.unit())
|
||||
.unwrap();
|
||||
let t0_out = pool.balance_0 * shares.value / pool.total_shares;
|
||||
let t1_out = pool.balance_1 * shares.value / pool.total_shares;
|
||||
pool.balance_0 -= t0_out;
|
||||
pool.balance_1 -= t1_out;
|
||||
pool.total_shares -= shares.value;
|
||||
|
||||
self.shares_to_redeem
|
||||
.push(remove_liquidity.to_output(t0_out, pair.t0, self.zone_id));
|
||||
|
||||
self.shares_to_redeem
|
||||
.push(remove_liquidity.to_output(t1_out, pair.t1, self.zone_id));
|
||||
}
|
||||
|
||||
pub fn process_op(&mut self, op: &ZoneOp) {
|
||||
match op {
|
||||
ZoneOp::Swap(swap) => self.swap(&swap),
|
||||
ZoneOp::AddLiquidity {
|
||||
tx, add_liquidity, ..
|
||||
} => {
|
||||
self.add_liquidity(&add_liquidity);
|
||||
assert!(self.validate_no_pools(&tx));
|
||||
// TODo: check proof
|
||||
}
|
||||
ZoneOp::RemoveLiquidity {
|
||||
tx,
|
||||
remove_liquidity,
|
||||
..
|
||||
} => {
|
||||
self.remove_liquidity(&remove_liquidity);
|
||||
assert!(self.validate_no_pools(&tx));
|
||||
// TODO: check proof
|
||||
}
|
||||
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);
|
||||
}
|
||||
pub fn check_swaps_executed(&self, tx: &Tx) {
|
||||
let zone_update = tx.updates.get(&self.zone_id).unwrap();
|
||||
for output in &self.swaps_output {
|
||||
assert!(zone_update.has_output(&output.note_commitment()));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_and_commit(mut self, updates: &StateUpdate) -> [u8; 32] {
|
||||
self.pools_update(&updates.tx, &updates.pool_notes);
|
||||
self.check_minted_shares(&updates.tx);
|
||||
self.check_redeemed_shares(&updates.tx);
|
||||
pub fn update_and_commit(mut self, tx: &Tx, pool_notes: &[InputWitness]) -> [u8; 32] {
|
||||
self.pools_update(&tx, pool_notes);
|
||||
self.check_swaps_executed(&tx);
|
||||
self.commit()
|
||||
}
|
||||
|
||||
@ -336,12 +276,18 @@ impl ZoneData {
|
||||
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(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.update(self.zone_id);
|
||||
hasher.finalize().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ZoneData {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
45
emmarin/apps/swapvm/app/tests/pricing.rs
Normal file
45
emmarin/apps/swapvm/app/tests/pricing.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use app::ZoneData;
|
||||
use cl::crust::UnitWitness;
|
||||
|
||||
fn nmo() -> UnitWitness {
|
||||
UnitWitness::nop(b"NMO")
|
||||
}
|
||||
fn mem() -> UnitWitness {
|
||||
UnitWitness::nop(b"MEM")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pair_price() {
|
||||
let mut swapvm_state = ZoneData::new();
|
||||
|
||||
// initially there is no NMO/MEM pair
|
||||
assert_eq!(swapvm_state.pair_price(nmo().unit(), mem().unit()), None);
|
||||
|
||||
swapvm_state.add_liquidity(nmo().unit(), mem().unit(), 10, 100);
|
||||
|
||||
// given that there is 1nmo:10mem in the pool, the price should show that we get 10 MEM for 1 NMO
|
||||
assert_eq!(
|
||||
swapvm_state.pair_price(nmo().unit(), mem().unit()),
|
||||
Some(10.0)
|
||||
);
|
||||
|
||||
// switching the trade direction should flip the price as well
|
||||
assert_eq!(
|
||||
swapvm_state.pair_price(mem().unit(), nmo().unit()),
|
||||
Some(0.1)
|
||||
);
|
||||
|
||||
// Due to slippage, the amount we get out is less than what the price would imply
|
||||
assert_eq!(
|
||||
swapvm_state.amount_out(nmo().unit(), mem().unit(), 1),
|
||||
Some(9) // 1 MEM slippage
|
||||
);
|
||||
assert_eq!(
|
||||
swapvm_state.amount_out(nmo().unit(), mem().unit(), 2),
|
||||
Some(16) // 4 MEM slippage
|
||||
);
|
||||
assert_eq!(
|
||||
swapvm_state.amount_out(nmo().unit(), mem().unit(), 5),
|
||||
Some(33) // 17 MEM slippage
|
||||
);
|
||||
}
|
||||
@ -4,6 +4,7 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.8"
|
||||
methods = { path = "../methods" }
|
||||
risc0-zkvm = { version = "1.2.0" }
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
@ -11,3 +12,4 @@ serde = "1.0"
|
||||
ledger_proof_statements = { path = "../../../../cl/ledger_proof_statements" }
|
||||
app = { path = "../../app" }
|
||||
cl = { path = "../../../../cl/cl" }
|
||||
ledger = { path = "../../../../cl/ledger" }
|
||||
|
||||
@ -1,33 +1,196 @@
|
||||
use app::{StateUpdate, ZoneOp};
|
||||
use cl::mantle::{ledger::Ledger, zone::ZoneData};
|
||||
use ledger_proof_statements::ledger::SyncLog;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use app::{swap_goal_unit, SwapArgs, ZoneData};
|
||||
use cl::crust::{BundleWitness, InputWitness, NoteCommitment, Nullifier, Tx, TxWitness, Unit};
|
||||
use cl::ds::mmr::{MMRFolds, MMRProof, MMR};
|
||||
use cl::mantle::ledger::{Ledger, LedgerState, LedgerWitness};
|
||||
use cl::mantle::ZoneState;
|
||||
use ledger::stf::{risc0_stf, StfProof};
|
||||
use methods::{STF_ELF, STF_ID};
|
||||
use risc0_zkvm::{ExecutorEnv, Prover, Receipt, Result};
|
||||
use risc0_zkvm::{ExecutorEnv, Prover, Result};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FundNote {
|
||||
note: InputWitness,
|
||||
mmr: MMR,
|
||||
path: MMRProof,
|
||||
}
|
||||
|
||||
impl FundNote {
|
||||
fn evolve(&self, new_amount: u64) -> InputWitness {
|
||||
let mut new_note = self.note.clone();
|
||||
new_note.value = new_amount;
|
||||
new_note.nonce = new_note.evolved_nonce(b"SWAP");
|
||||
new_note
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ExecutorState {
|
||||
pub ledger: LedgerState,
|
||||
pub swapvm: ZoneData,
|
||||
fund_notes: BTreeMap<Unit, FundNote>,
|
||||
goal_notes: Vec<(InputWitness, MMR, MMRProof)>,
|
||||
}
|
||||
|
||||
impl ExecutorState {
|
||||
pub fn zone_state(&self) -> ZoneState {
|
||||
ZoneState {
|
||||
stf: risc0_stf(STF_ID),
|
||||
zone_data: self.swapvm.commit(),
|
||||
ledger: self.ledger.to_witness().commit(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn observe_cm(&mut self, cm: &NoteCommitment) -> ((MMR, MMRProof), MMRFolds) {
|
||||
let folds = self.ledger.commitments.folds(&cm.0);
|
||||
|
||||
for (_, fund_note) in self.fund_notes.iter_mut() {
|
||||
assert_eq!(fund_note.mmr, self.ledger.commitments);
|
||||
fund_note
|
||||
.path
|
||||
.update(&fund_note.note.note_commitment().0, &folds);
|
||||
}
|
||||
|
||||
let proof = self.ledger.add_commitment(cm);
|
||||
|
||||
for (_, fund_note) in self.fund_notes.iter_mut() {
|
||||
fund_note.mmr = self.ledger.commitments.clone();
|
||||
}
|
||||
|
||||
(proof, folds)
|
||||
}
|
||||
|
||||
pub fn observe_nfs(&mut self, nfs: Vec<Nullifier>) {
|
||||
self.ledger.add_nullifiers(nfs);
|
||||
}
|
||||
|
||||
pub fn process_tx(&mut self, tx: &Tx) {
|
||||
let Some(swapvm_update) = tx.updates.get(&self.swapvm.zone_id) else {
|
||||
// this tx is not related to the swapvm zone
|
||||
return;
|
||||
};
|
||||
|
||||
let mut output_mmr_proofs = BTreeMap::<NoteCommitment, MMRProof>::new();
|
||||
|
||||
for (cm, _) in &swapvm_update.outputs {
|
||||
let (proof, folds) = self.observe_cm(cm);
|
||||
|
||||
for (other_cm, other_cm_proof) in output_mmr_proofs.iter_mut() {
|
||||
other_cm_proof.update(&other_cm.0, &folds);
|
||||
}
|
||||
|
||||
output_mmr_proofs.insert(*cm, proof.1);
|
||||
|
||||
for (other_cm, other_cm_proof) in &output_mmr_proofs {
|
||||
assert!(self
|
||||
.ledger
|
||||
.commitments
|
||||
.verify_proof(&other_cm.0, &other_cm_proof))
|
||||
}
|
||||
}
|
||||
|
||||
if tx.balance.unit_balance(swap_goal_unit().unit()).is_neg() {
|
||||
// this is a SWAP
|
||||
let (swap_goal_cm, swap_args_bytes) = &swapvm_update.outputs[0];
|
||||
let swap_args: SwapArgs = cl::deserialize(&swap_args_bytes);
|
||||
|
||||
// verify the user proved the correct swap goal note
|
||||
let swap_goal_witness = app::swap_goal_note(swap_args.nonce);
|
||||
assert_eq!(swap_goal_cm, &swap_goal_witness.note_commitment());
|
||||
|
||||
self.goal_notes.push((
|
||||
swap_goal_witness,
|
||||
self.ledger.commitments.clone(),
|
||||
output_mmr_proofs[swap_goal_cm].clone(),
|
||||
));
|
||||
|
||||
// assume there are only the goal unit and tokenIn units at play
|
||||
assert_eq!(tx.balance.balances.len(), 2);
|
||||
|
||||
let balance_in = tx
|
||||
.balance
|
||||
.balances
|
||||
.iter()
|
||||
.find(|bal| bal.unit != swap_goal_unit().unit())
|
||||
.unwrap();
|
||||
|
||||
let token_in = balance_in.unit;
|
||||
assert_eq!(balance_in.neg, 0);
|
||||
assert!(balance_in.pos > 0);
|
||||
|
||||
let amount_in = balance_in.pos;
|
||||
self.swapvm.swap(token_in, amount_in, swap_args);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_and_get_executor_tx(&mut self) -> (TxWitness, Vec<InputWitness>) {
|
||||
let mut tx = TxWitness::default();
|
||||
let mut new_fund_notes = Vec::new();
|
||||
|
||||
let expected_pool_balances = self.swapvm.expected_pool_balances();
|
||||
let fund_notes = std::mem::take(&mut self.fund_notes);
|
||||
|
||||
for note in &self.swapvm.swaps_output {
|
||||
tx = tx.add_output(note.clone(), "");
|
||||
self.ledger.add_commitment(¬e.note_commitment());
|
||||
}
|
||||
|
||||
self.swapvm.nfs.clear();
|
||||
for (unit, value) in expected_pool_balances {
|
||||
let note = if let Some(note) = fund_notes.get(&unit) {
|
||||
note.evolve(value)
|
||||
} else {
|
||||
panic!("dynamically created fund notes are not supported");
|
||||
};
|
||||
new_fund_notes.push(note);
|
||||
let output = note.to_output();
|
||||
tx = tx.add_output(output, "");
|
||||
let (mmr, path) = self.ledger.add_commitment(&output.note_commitment());
|
||||
self.fund_notes
|
||||
.insert(note.unit_witness.unit(), FundNote { note, mmr, path });
|
||||
self.swapvm.nfs.insert(note.nullifier());
|
||||
}
|
||||
|
||||
for (_, FundNote { note, mmr, path }) in fund_notes.into_iter() {
|
||||
tx = tx.add_input(note, (mmr, path));
|
||||
}
|
||||
|
||||
for (goal_note, mmr, path) in std::mem::take(&mut self.goal_notes) {
|
||||
tx = tx.add_input(goal_note, (mmr, path));
|
||||
}
|
||||
(tx, new_fund_notes)
|
||||
}
|
||||
|
||||
pub fn set_fund_note(&mut self, note: InputWitness, mmr: MMR, path: MMRProof) {
|
||||
self.fund_notes
|
||||
.insert(note.unit_witness.unit(), FundNote { note, mmr, path });
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StfPrivate {
|
||||
pub zone_data: ZoneData,
|
||||
pub old_ledger: Ledger,
|
||||
pub old_ledger: LedgerWitness,
|
||||
pub new_ledger: Ledger,
|
||||
pub sync_logs: Vec<SyncLog>,
|
||||
pub ops: Vec<ZoneOp>,
|
||||
pub update_tx: StateUpdate,
|
||||
pub fund_notes: Vec<InputWitness>,
|
||||
pub bundle: BundleWitness,
|
||||
}
|
||||
|
||||
impl StfPrivate {
|
||||
pub fn prove(&self, prover: &impl Prover) -> Result<Receipt> {
|
||||
pub fn prove(&self, prover: &dyn Prover) -> Result<StfProof> {
|
||||
let env = ExecutorEnv::builder()
|
||||
.write(&self.zone_data)?
|
||||
.write(&self.old_ledger)?
|
||||
.write(&self.new_ledger)?
|
||||
.write(&self.sync_logs)?
|
||||
.write(&STF_ID)?
|
||||
.write(&self.ops)?
|
||||
.write(&self.update_tx)?
|
||||
.write(&unsafe { std::intrinsics::transmute::<_, [u8; 32]>(STF_ID) })?
|
||||
.write(&self.bundle)?
|
||||
.write(&self.fund_notes)?
|
||||
.build()?;
|
||||
|
||||
let prove_info = prover.prove(env, STF_ELF)?;
|
||||
|
||||
debug_assert!(prove_info.receipt.verify(STF_ID).is_ok());
|
||||
Ok(prove_info.receipt)
|
||||
|
||||
Ok(StfProof::from_risc0(risc0_stf(STF_ID), prove_info.receipt))
|
||||
}
|
||||
}
|
||||
|
||||
156
emmarin/apps/swapvm/stf/host/tests/swap.rs
Normal file
156
emmarin/apps/swapvm/stf/host/tests/swap.rs
Normal file
@ -0,0 +1,156 @@
|
||||
use app::ZONE_ID;
|
||||
use cl::crust::{BundleWitness, InputWitness, Nonce, NullifierSecret, TxWitness, UnitWitness};
|
||||
use cl::mantle::ledger::LedgerState;
|
||||
use cl::mantle::update::{BatchUpdate, Update};
|
||||
use host::{ExecutorState, StfPrivate};
|
||||
use ledger::ledger::ProvedLedgerTransition;
|
||||
use ledger::update::ProvedBatchUpdate;
|
||||
use ledger::{bundle::ProvedBundle, tx::ProvedTx};
|
||||
use rand::RngCore;
|
||||
|
||||
fn nmo() -> UnitWitness {
|
||||
UnitWitness::nop(b"NMO")
|
||||
}
|
||||
fn mem() -> UnitWitness {
|
||||
UnitWitness::nop(b"MEM")
|
||||
}
|
||||
|
||||
fn setup_executor(mut rng: impl RngCore, ledger: LedgerState) -> ExecutorState {
|
||||
let mut exec_state = ExecutorState::default();
|
||||
exec_state.ledger = ledger;
|
||||
|
||||
let nmo_fund = InputWitness {
|
||||
state: [0u8; 32],
|
||||
value: 1348,
|
||||
unit_witness: nmo(),
|
||||
nonce: Nonce::random(&mut rng),
|
||||
zone_id: ZONE_ID,
|
||||
nf_sk: NullifierSecret::zero(),
|
||||
};
|
||||
|
||||
let ((mmr, mmr_proof), _) = exec_state.observe_cm(&nmo_fund.note_commitment());
|
||||
exec_state.set_fund_note(nmo_fund, mmr, mmr_proof);
|
||||
|
||||
let mem_fund = InputWitness {
|
||||
state: [0u8; 32],
|
||||
value: 14102,
|
||||
unit_witness: mem(),
|
||||
nonce: Nonce::random(&mut rng),
|
||||
zone_id: ZONE_ID,
|
||||
nf_sk: NullifierSecret::zero(),
|
||||
};
|
||||
let ((mmr, mmr_proof), _) = exec_state.observe_cm(&mem_fund.note_commitment());
|
||||
exec_state.set_fund_note(mem_fund, mmr, mmr_proof);
|
||||
|
||||
// HACK: we don't currently support liquidity notes, we directly hard code the corresponding liquidity
|
||||
// in the swapvm instead of minting pool LP tokens
|
||||
exec_state
|
||||
.swapvm
|
||||
.add_liquidity(nmo().unit(), mem().unit(), nmo_fund.value, mem_fund.value);
|
||||
|
||||
exec_state
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_swap() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let ledger = LedgerState::default();
|
||||
|
||||
// ---- setup scenario ----
|
||||
|
||||
let alice_sk = NullifierSecret::random(&mut rng);
|
||||
|
||||
let alice_in = InputWitness {
|
||||
state: [0u8; 32],
|
||||
value: 10,
|
||||
unit_witness: nmo(),
|
||||
nonce: Nonce::random(&mut rng),
|
||||
zone_id: ZONE_ID,
|
||||
nf_sk: alice_sk,
|
||||
};
|
||||
|
||||
let mut exec_state = setup_executor(&mut rng, ledger);
|
||||
let (alice_in_proof, _) = exec_state.observe_cm(&alice_in.note_commitment());
|
||||
|
||||
// ----- end setup ----
|
||||
// Alice now has a valid 10 NMO note, she wants to swap it for 90 MEM
|
||||
// ---- begin swap ----
|
||||
|
||||
let old_zone_state = exec_state.zone_state();
|
||||
let old_zone_data = exec_state.swapvm.clone();
|
||||
|
||||
let mut temp_ledger_state = exec_state.ledger.clone();
|
||||
|
||||
let swap_goal_nonce = Nonce::random(&mut rng);
|
||||
let swap_tx = TxWitness::default()
|
||||
.add_input(alice_in, alice_in_proof)
|
||||
.add_output(
|
||||
app::swap_goal_note(swap_goal_nonce).to_output(),
|
||||
app::SwapArgs {
|
||||
output: app::SwapOutput::basic(mem().unit(), ZONE_ID, alice_sk.commit(), &mut rng),
|
||||
limit: 90,
|
||||
nonce: swap_goal_nonce,
|
||||
},
|
||||
);
|
||||
|
||||
let swap_tx_proof = ProvedTx::prove(swap_tx, vec![], vec![]).unwrap();
|
||||
|
||||
//
|
||||
// alice ---- (swap_tx, swap_tx_proof) ---> executor
|
||||
//
|
||||
// alice sends the tx to an executor
|
||||
exec_state.process_tx(&swap_tx_proof.public());
|
||||
|
||||
// the executor builds the solving tx
|
||||
let (exec_tx, fund_notes) = exec_state.update_and_get_executor_tx();
|
||||
let proved_exec_tx = ProvedTx::prove(exec_tx, vec![], vec![]).unwrap();
|
||||
|
||||
let swap_bundle = BundleWitness {
|
||||
txs: vec![swap_tx_proof.public(), proved_exec_tx.public()],
|
||||
};
|
||||
|
||||
let swap_bundle_proof = ProvedBundle::prove(vec![swap_tx_proof, proved_exec_tx]);
|
||||
exec_state.ledger.add_bundle(swap_bundle.root());
|
||||
exec_state.observe_nfs(
|
||||
swap_bundle
|
||||
.clone()
|
||||
.commit()
|
||||
.updates
|
||||
.get(&exec_state.swapvm.zone_id)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.flat_map(|u| u.inputs.iter().copied())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
// prove stf
|
||||
let stf_proof = StfPrivate {
|
||||
zone_data: old_zone_data,
|
||||
old_ledger: temp_ledger_state.to_witness(),
|
||||
new_ledger: exec_state.ledger.clone().to_witness().commit(),
|
||||
fund_notes,
|
||||
bundle: swap_bundle,
|
||||
}
|
||||
.prove(risc0_zkvm::default_prover().as_ref())
|
||||
.unwrap();
|
||||
|
||||
let ledger_proof =
|
||||
ProvedLedgerTransition::prove(&mut temp_ledger_state, ZONE_ID, vec![swap_bundle_proof]);
|
||||
|
||||
let new_zone_state = exec_state.zone_state();
|
||||
|
||||
assert_eq!(ledger_proof.public().old_ledger, old_zone_state.ledger);
|
||||
assert_eq!(ledger_proof.public().ledger, new_zone_state.ledger);
|
||||
|
||||
let zone_update = ProvedBatchUpdate {
|
||||
batch: BatchUpdate {
|
||||
updates: vec![Update {
|
||||
old: old_zone_state,
|
||||
new: new_zone_state,
|
||||
}],
|
||||
},
|
||||
ledger_proofs: vec![ledger_proof],
|
||||
stf_proofs: vec![stf_proof],
|
||||
};
|
||||
|
||||
assert!(zone_update.verify())
|
||||
}
|
||||
@ -10,4 +10,4 @@ risc0-zkvm = { version = "1.2.0", default-features = false, features = ['std'] }
|
||||
app = { path = "../../../app" }
|
||||
ledger_proof_statements = { path = "../../../../../cl/ledger_proof_statements" }
|
||||
cl = { path = "../../../../../cl/cl" }
|
||||
ledger_validity_proof = { path = "../../../../../cl/ledger_validity_proof" }
|
||||
ledger_risc0_proof = { path = "../../../../../cl/ledger_risc0_proof" }
|
||||
@ -1,79 +1,81 @@
|
||||
use app::{StateUpdate, ZoneData, ZoneOp};
|
||||
use app::{SwapArgs, ZoneData};
|
||||
use cl::{
|
||||
crust::Tx,
|
||||
mantle::{ledger::Ledger, zone::ZoneState},
|
||||
};
|
||||
use ledger_proof_statements::{
|
||||
ledger::{LedgerProofPublic, SyncLog},
|
||||
stf::StfPublic,
|
||||
crust::{BundleWitness, InputWitness},
|
||||
mantle::{
|
||||
ledger::{Ledger, LedgerWitness},
|
||||
zone::ZoneState,
|
||||
},
|
||||
};
|
||||
use ledger_proof_statements::stf::StfPublic;
|
||||
use risc0_zkvm::guest::env;
|
||||
|
||||
fn main() {
|
||||
let mut zone_data: ZoneData = env::read();
|
||||
let old_ledger: Ledger = env::read();
|
||||
let ledger: Ledger = env::read();
|
||||
let sync_logs: Vec<SyncLog> = env::read();
|
||||
let mut ledger_witness: LedgerWitness = env::read();
|
||||
let new_ledger: Ledger = env::read();
|
||||
let stf: [u8; 32] = env::read();
|
||||
let ops: Vec<ZoneOp> = env::read();
|
||||
let update_tx: StateUpdate = env::read();
|
||||
let mut bundle: BundleWitness = env::read();
|
||||
let pools_notes: Vec<InputWitness> = env::read();
|
||||
|
||||
let zone_id = zone_data.zone_id;
|
||||
|
||||
let old_zone_data = zone_data.commit();
|
||||
|
||||
for op in &ops {
|
||||
zone_data.process_op(op);
|
||||
}
|
||||
|
||||
let txs: Vec<&Tx> = ops
|
||||
.iter()
|
||||
.filter_map(|op| match op {
|
||||
ZoneOp::Swap(_) => None,
|
||||
ZoneOp::AddLiquidity { tx, .. } => Some(tx),
|
||||
ZoneOp::RemoveLiquidity { tx, .. } => Some(tx),
|
||||
ZoneOp::Ledger(tx) => Some(tx),
|
||||
})
|
||||
.chain(std::iter::once(&update_tx.tx))
|
||||
.collect();
|
||||
|
||||
let outputs = txs
|
||||
.iter()
|
||||
.flat_map(|tx| tx.updates.iter().filter(|u| u.zone_id == zone_id))
|
||||
.flat_map(|u| u.outputs.iter())
|
||||
.copied()
|
||||
.collect();
|
||||
// TODO: inputs missings from ledger proof public
|
||||
let _inputs: Vec<_> = txs
|
||||
.iter()
|
||||
.flat_map(|tx| tx.updates.iter().filter(|u| u.zone_id == zone_id))
|
||||
.flat_map(|u| u.inputs.iter())
|
||||
.copied()
|
||||
.collect();
|
||||
|
||||
let ledger_public = LedgerProofPublic {
|
||||
old_ledger,
|
||||
ledger,
|
||||
id: zone_id,
|
||||
sync_logs,
|
||||
outputs,
|
||||
let old_state = ZoneState {
|
||||
ledger: ledger_witness.commit(),
|
||||
zone_data: zone_data.commit(),
|
||||
stf,
|
||||
};
|
||||
|
||||
env::verify(
|
||||
ledger_validity_proof::LEDGER_ID,
|
||||
&risc0_zkvm::serde::to_vec(&ledger_public).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
ledger_witness.add_bundle(bundle.root());
|
||||
// ensure that we've seen every bundle in this ledger update
|
||||
assert_eq!(ledger_witness.bundles.commit(), new_ledger.bundles_root);
|
||||
|
||||
// The last bundle should be a single executor tx that updates the zone data
|
||||
let executor_tx = bundle.txs.pop().unwrap();
|
||||
for tx in bundle.txs {
|
||||
let Some(zone_update) = tx.updates.get(&zone_id) else {
|
||||
// this tx does not concern this zone, ignore it.
|
||||
continue;
|
||||
};
|
||||
|
||||
assert!(zone_data.validate_no_pools(zone_update));
|
||||
|
||||
// is it a SWAP?
|
||||
if tx
|
||||
.balance
|
||||
.unit_balance(app::swap_goal_unit().unit())
|
||||
.is_neg()
|
||||
{
|
||||
// This TX encodes a SWAP request.
|
||||
// as a simplifying assumption, we will assume that the SWAP goal note is the only output
|
||||
// and a single input represents the funds provided by the user for the swap.
|
||||
assert_eq!(zone_update.outputs.len(), 1);
|
||||
assert_eq!(zone_update.inputs.len(), 1);
|
||||
let (swap_goal_cm, swap_args_bytes) = &zone_update.outputs[0];
|
||||
let swap_args: SwapArgs = cl::deserialize(&swap_args_bytes);
|
||||
|
||||
// ensure the witness corresponds to the swap goal cm
|
||||
assert_eq!(
|
||||
swap_goal_cm,
|
||||
&app::swap_goal_note(swap_args.nonce).note_commitment()
|
||||
);
|
||||
|
||||
let funds = tx
|
||||
.balance
|
||||
.balances
|
||||
.iter()
|
||||
.find(|bal| bal.unit != app::swap_goal_unit().unit())
|
||||
.unwrap();
|
||||
let t_in = funds.unit;
|
||||
let amount_in = funds.pos - funds.neg;
|
||||
zone_data.swap(t_in, amount_in, swap_args);
|
||||
}
|
||||
}
|
||||
|
||||
let public = StfPublic {
|
||||
old: ZoneState {
|
||||
ledger: old_ledger,
|
||||
zone_data: old_zone_data,
|
||||
stf,
|
||||
},
|
||||
old: old_state,
|
||||
new: ZoneState {
|
||||
ledger,
|
||||
zone_data: zone_data.update_and_commit(&update_tx),
|
||||
ledger: new_ledger,
|
||||
zone_data: zone_data.update_and_commit(&executor_tx, &pools_notes),
|
||||
stf,
|
||||
},
|
||||
};
|
||||
|
||||
@ -4,12 +4,10 @@ members = [
|
||||
"cl",
|
||||
"ledger",
|
||||
"ledger_proof_statements",
|
||||
"risc0_proofs",
|
||||
"ledger_risc0_proof",
|
||||
"bundle_risc0_proof",
|
||||
"tx_risc0_proof",
|
||||
"ledger_validity_proof",
|
||||
"risc0_proofs",
|
||||
"risc0_images",
|
||||
"risc0_images_police",
|
||||
]
|
||||
|
||||
# Always optimize; building and running the risc0_proofs takes much longer without optimization.
|
||||
|
||||
@ -1,11 +1,17 @@
|
||||
[package]
|
||||
name = "nomos_mantle_bundle_risc0_proof"
|
||||
name = "bundle_risc0_proof"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[build-dependencies]
|
||||
risc0-build = { version = "1.0" }
|
||||
[dependencies]
|
||||
risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
|
||||
cl = { path = "../cl" }
|
||||
risc0_images = { path = "../risc0_images" }
|
||||
hex = "0.4"
|
||||
|
||||
[package.metadata.risc0]
|
||||
methods = ["bundle"]
|
||||
|
||||
[patch.crates-io]
|
||||
# add RISC Zero accelerator support for all downstream usages of the following crates.
|
||||
sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.8-risczero.0" }
|
||||
crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.5-risczero.0" }
|
||||
curve25519-dalek = { git = "https://github.com/risc0/curve25519-dalek", tag = "curve25519-4.1.2-risczero.0" }
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
fn main() {
|
||||
risc0_build::embed_methods();
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
[package]
|
||||
name = "bundle"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
cl = { path = "../../cl" }
|
||||
ledger_proof_statements = { path = "../../ledger_proof_statements" }
|
||||
risc0_images = { path = "../../risc0_images" }
|
||||
|
||||
|
||||
[patch.crates-io]
|
||||
# add RISC Zero accelerator support for all downstream usages of the following crates.
|
||||
sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.8-risczero.0" }
|
||||
crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.5-risczero.0" }
|
||||
curve25519-dalek = { git = "https://github.com/risc0/curve25519-dalek", tag = "curve25519-4.1.2-risczero.0" }
|
||||
@ -1,4 +1,5 @@
|
||||
use cl::crust::BundleWitness;
|
||||
use hex::FromHex;
|
||||
use risc0_zkvm::{guest::env, serde};
|
||||
|
||||
fn main() {
|
||||
@ -6,7 +7,7 @@ fn main() {
|
||||
|
||||
for tx in &bundle_private.txs {
|
||||
env::verify(
|
||||
risc0_images::nomos_mantle_tx_risc0_proof::TX_ID,
|
||||
<[u8; 32]>::from_hex(risc0_images::TX_ID).unwrap(),
|
||||
&serde::to_vec(&tx).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
@ -1 +0,0 @@
|
||||
include!(concat!(env!("OUT_DIR"), "/methods.rs"));
|
||||
@ -3,22 +3,15 @@ name = "cl"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde = {version="1.0", features = ["derive"]}
|
||||
group = "0.13.0"
|
||||
rand = "0.8.5"
|
||||
rand_core = "0.6.0"
|
||||
hex = "0.4.3"
|
||||
curve25519-dalek = {version = "4.1", features = ["serde", "digest", "rand_core"]}
|
||||
sha2 = "0.10"
|
||||
lazy_static = "1.5.0"
|
||||
risc0-zkvm = "1.2"
|
||||
itertools = "0.14"
|
||||
digest = "0.10"
|
||||
|
||||
bincode = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8.5"
|
||||
proptest = "1.2.0"
|
||||
proptest-macro = "0.1"
|
||||
@ -14,6 +14,15 @@ pub struct UnitWitness {
|
||||
}
|
||||
|
||||
impl UnitWitness {
|
||||
pub fn nop(args: &[u8]) -> Self {
|
||||
Self {
|
||||
spending_covenant: NOP_COVENANT,
|
||||
minting_covenant: NOP_COVENANT,
|
||||
burning_covenant: NOP_COVENANT,
|
||||
arg: crate::hash(args),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unit(&self) -> Unit {
|
||||
let mut hasher = Hash::new();
|
||||
hasher.update(b"NOMOS_CL_UNIT");
|
||||
@ -33,24 +42,32 @@ pub struct UnitBalance {
|
||||
}
|
||||
|
||||
impl UnitBalance {
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.pos == self.neg
|
||||
}
|
||||
|
||||
pub fn pos(unit: Unit, value: u64) -> Self {
|
||||
pub fn zero(unit: Unit) -> Self {
|
||||
Self {
|
||||
unit,
|
||||
pos: value,
|
||||
pos: 0,
|
||||
neg: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn neg(unit: Unit, value: u64) -> Self {
|
||||
Self {
|
||||
unit,
|
||||
pos: 0,
|
||||
neg: value,
|
||||
}
|
||||
pub fn pos(unit: Unit, pos: u64) -> Self {
|
||||
Self { unit, pos, neg: 0 }
|
||||
}
|
||||
|
||||
pub fn neg(unit: Unit, neg: u64) -> Self {
|
||||
Self { unit, pos: 0, neg }
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.pos == self.neg
|
||||
}
|
||||
|
||||
pub fn is_neg(&self) -> bool {
|
||||
self.neg > self.pos
|
||||
}
|
||||
|
||||
pub fn is_pos(&self) -> bool {
|
||||
self.pos > self.neg
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,6 +82,14 @@ impl Balance {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unit_balance(&self, unit: Unit) -> UnitBalance {
|
||||
self.balances
|
||||
.iter()
|
||||
.find(|b| b.unit == unit)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| UnitBalance::zero(unit))
|
||||
}
|
||||
|
||||
pub fn insert_positive(&mut self, unit: Unit, value: Value) {
|
||||
for unit_bal in self.balances.iter_mut() {
|
||||
if unit_bal.unit == unit {
|
||||
|
||||
@ -6,7 +6,7 @@ use crate::{
|
||||
mantle::ZoneId,
|
||||
Digest, Hash,
|
||||
};
|
||||
use rand::RngCore;
|
||||
use rand_core::RngCore;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
use crate::crust::{balance::Unit, nullifier::NullifierCommitment};
|
||||
use crate::mantle::ZoneId;
|
||||
use crate::{Digest, Hash};
|
||||
use rand::RngCore;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -15,7 +15,7 @@ use crate::{
|
||||
};
|
||||
|
||||
/// An identifier of a transaction
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)]
|
||||
pub struct TxRoot(pub [u8; 32]);
|
||||
|
||||
/// An identifier of a bundle
|
||||
@ -50,7 +50,8 @@ impl TxRoot {
|
||||
pub struct Tx {
|
||||
pub root: TxRoot,
|
||||
pub balance: Balance,
|
||||
pub updates: Vec<LedgerUpdate>,
|
||||
pub updates: BTreeMap<ZoneId, LedgerUpdate>,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
@ -63,23 +64,15 @@ pub struct TxWitness {
|
||||
pub frontier_paths: Vec<(MMR, MMRProof)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
pub struct LedgerUpdate {
|
||||
pub zone_id: ZoneId,
|
||||
pub frontier_nodes: Vec<Root>,
|
||||
pub inputs: Vec<Nullifier>,
|
||||
pub outputs: Vec<NoteCommitment>,
|
||||
}
|
||||
|
||||
pub struct LedgerUpdateWitness {
|
||||
pub zone_id: ZoneId,
|
||||
pub frontier_nodes: Vec<Root>,
|
||||
pub inputs: Vec<Nullifier>,
|
||||
pub outputs: Vec<(NoteCommitment, Vec<u8>)>,
|
||||
}
|
||||
|
||||
impl LedgerUpdateWitness {
|
||||
pub fn commit(self) -> (LedgerUpdate, [u8; 32]) {
|
||||
impl LedgerUpdate {
|
||||
pub fn root(&self, zone_id: ZoneId) -> [u8; 32] {
|
||||
let input_root = merkle::root(&merkle::padded_leaves(&self.inputs));
|
||||
let output_root = merkle::root(&merkle::padded_leaves(self.outputs.iter().map(
|
||||
|(cm, data)| {
|
||||
@ -88,67 +81,83 @@ impl LedgerUpdateWitness {
|
||||
.collect::<Vec<_>>()
|
||||
},
|
||||
)));
|
||||
let root = merkle::root(&merkle::padded_leaves([
|
||||
input_root,
|
||||
output_root,
|
||||
self.zone_id,
|
||||
]));
|
||||
merkle::root(&merkle::padded_leaves([zone_id, input_root, output_root]))
|
||||
}
|
||||
|
||||
(
|
||||
LedgerUpdate {
|
||||
zone_id: self.zone_id,
|
||||
inputs: self.inputs,
|
||||
outputs: self.outputs.into_iter().map(|(cm, _)| cm).collect(),
|
||||
frontier_nodes: self.frontier_nodes,
|
||||
},
|
||||
root,
|
||||
)
|
||||
pub fn add_input(&mut self, nf: Nullifier, mmr: MMR) -> &mut Self {
|
||||
self.inputs.push(nf);
|
||||
self.frontier_nodes.extend(mmr.roots);
|
||||
self.frontier_nodes.sort();
|
||||
self.frontier_nodes.dedup();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_output(&mut self, cm: NoteCommitment, data: Vec<u8>) -> &mut Self {
|
||||
self.outputs.push((cm, data));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn has_input(&self, nf: &Nullifier) -> bool {
|
||||
self.inputs.contains(nf)
|
||||
}
|
||||
|
||||
pub fn has_output(&self, cm: &NoteCommitment) -> bool {
|
||||
self.outputs.iter().any(|(out_cm, _data)| out_cm == cm)
|
||||
}
|
||||
}
|
||||
|
||||
impl TxWitness {
|
||||
pub fn add_input(mut self, input: InputWitness, input_cm_proof: (MMR, MMRProof)) -> Self {
|
||||
assert!(input_cm_proof
|
||||
.0
|
||||
.verify_proof(&input.note_commitment().0, &input_cm_proof.1));
|
||||
|
||||
for (i, other_input) in self.inputs.iter().enumerate() {
|
||||
if other_input.zone_id == input.zone_id {
|
||||
// ensure a single MMR per zone per tx
|
||||
assert_eq!(self.frontier_paths[i].0, input_cm_proof.0);
|
||||
}
|
||||
}
|
||||
|
||||
self.inputs.push(input);
|
||||
self.frontier_paths.push(input_cm_proof);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_output(mut self, output: OutputWitness, data: Vec<u8>) -> Self {
|
||||
self.outputs.push((output, data));
|
||||
pub fn add_output(mut self, output: OutputWitness, data: impl Serialize) -> Self {
|
||||
self.outputs.push((output, crate::serialize(data)));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn compute_updates(&self, inputs: &[InputDerivedFields]) -> Vec<LedgerUpdateWitness> {
|
||||
let mut updates = BTreeMap::new();
|
||||
assert_eq!(self.inputs.len(), self.frontier_paths.len());
|
||||
pub fn compute_updates(&self, inputs: &[InputDerivedFields]) -> BTreeMap<ZoneId, LedgerUpdate> {
|
||||
let mut updates: BTreeMap<ZoneId, LedgerUpdate> = Default::default();
|
||||
|
||||
assert_eq!(
|
||||
self.inputs.len(),
|
||||
self.frontier_paths.len(),
|
||||
"out: {} {}",
|
||||
self.inputs.len(),
|
||||
self.frontier_paths.len()
|
||||
);
|
||||
for (input, (mmr, path)) in inputs.iter().zip(&self.frontier_paths) {
|
||||
let entry = updates.entry(input.zone_id).or_insert(LedgerUpdateWitness {
|
||||
zone_id: input.zone_id,
|
||||
inputs: vec![],
|
||||
outputs: vec![],
|
||||
frontier_nodes: mmr.roots.clone(),
|
||||
});
|
||||
entry.inputs.push(input.nf);
|
||||
assert!(mmr.verify_proof(&input.cm.0, path));
|
||||
// ensure a single MMR per zone per tx
|
||||
assert_eq!(&mmr.roots, &entry.frontier_nodes);
|
||||
updates
|
||||
.entry(input.zone_id)
|
||||
.or_default()
|
||||
.add_input(input.nf, mmr.clone());
|
||||
}
|
||||
|
||||
for (output, data) in &self.outputs {
|
||||
assert!(output.value > 0);
|
||||
updates
|
||||
.entry(output.zone_id)
|
||||
.or_insert(LedgerUpdateWitness {
|
||||
zone_id: output.zone_id,
|
||||
inputs: vec![],
|
||||
outputs: vec![],
|
||||
frontier_nodes: vec![],
|
||||
})
|
||||
.outputs
|
||||
.push((output.note_commitment(), data.clone())); // TODO: avoid clone
|
||||
.or_default()
|
||||
.add_output(output.note_commitment(), data.clone()); // TODO: avoid clone
|
||||
}
|
||||
|
||||
updates.into_values().collect()
|
||||
updates
|
||||
}
|
||||
|
||||
pub fn mint_amounts(&self) -> Vec<MintAmount> {
|
||||
@ -232,12 +241,12 @@ impl TxWitness {
|
||||
) -> Tx {
|
||||
let mint_burn_root = Self::mint_burn_root(mints, burns);
|
||||
|
||||
let (updates, updates_roots): (Vec<_>, Vec<_>) = self
|
||||
.compute_updates(inputs)
|
||||
.into_iter()
|
||||
.map(LedgerUpdateWitness::commit)
|
||||
.unzip();
|
||||
let update_root = merkle::root(&merkle::padded_leaves(updates_roots));
|
||||
let updates = self.compute_updates(inputs);
|
||||
let update_root = merkle::root(&merkle::padded_leaves(
|
||||
updates
|
||||
.iter()
|
||||
.map(|(zone_id, update)| update.root(*zone_id)),
|
||||
));
|
||||
let root = self.root(update_root, mint_burn_root);
|
||||
let balance = self.balance(mints, burns);
|
||||
|
||||
@ -245,13 +254,14 @@ impl TxWitness {
|
||||
root,
|
||||
balance,
|
||||
updates,
|
||||
data: self.data.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Bundle {
|
||||
pub updates: Vec<LedgerUpdate>,
|
||||
pub updates: BTreeMap<ZoneId, Vec<LedgerUpdate>>,
|
||||
pub root: BundleRoot,
|
||||
}
|
||||
|
||||
@ -261,44 +271,26 @@ pub struct BundleWitness {
|
||||
}
|
||||
|
||||
impl BundleWitness {
|
||||
pub fn root(&self) -> BundleRoot {
|
||||
BundleRoot(merkle::root(&merkle::padded_leaves(
|
||||
self.txs.iter().map(|tx| tx.root.0),
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn commit(self) -> Bundle {
|
||||
assert!(Balance::combine(self.txs.iter().map(|tx| &tx.balance)).is_zero());
|
||||
|
||||
let root = BundleRoot(merkle::root(&merkle::padded_leaves(
|
||||
self.txs.iter().map(|tx| tx.root.0),
|
||||
)));
|
||||
let root = self.root();
|
||||
|
||||
let updates = self
|
||||
.txs
|
||||
.into_iter()
|
||||
.fold(BTreeMap::new(), |mut updates, tx| {
|
||||
for update in tx.updates {
|
||||
let entry = updates.entry(update.zone_id).or_insert(LedgerUpdate {
|
||||
zone_id: update.zone_id,
|
||||
inputs: vec![],
|
||||
outputs: vec![],
|
||||
frontier_nodes: vec![],
|
||||
});
|
||||
|
||||
entry.inputs.extend(update.inputs);
|
||||
entry.outputs.extend(update.outputs);
|
||||
entry.frontier_nodes.extend(update.frontier_nodes); // TODO: maybe merge?
|
||||
.fold(<BTreeMap<_, Vec<_>>>::new(), |mut updates, tx| {
|
||||
for (zone_id, update) in tx.updates {
|
||||
updates.entry(zone_id).or_default().push(update);
|
||||
}
|
||||
|
||||
updates
|
||||
})
|
||||
.into_values()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// de-dup frontier nodes
|
||||
let updates = updates
|
||||
.into_iter()
|
||||
.map(|mut update| {
|
||||
update.frontier_nodes.sort();
|
||||
update.frontier_nodes.dedup();
|
||||
update
|
||||
})
|
||||
.collect();
|
||||
});
|
||||
|
||||
Bundle { updates, root }
|
||||
}
|
||||
|
||||
@ -152,6 +152,22 @@ impl MMRProof {
|
||||
let leaf = merkle::leaf(elem);
|
||||
merkle::path_root(leaf, &self.path)
|
||||
}
|
||||
|
||||
pub fn update(&mut self, elem: &[u8], folds: &MMRFolds) {
|
||||
for (l, r) in &folds.folds {
|
||||
let root = self.root(elem);
|
||||
if &root == l {
|
||||
self.path.push(merkle::PathNode::Right(*r))
|
||||
} else if &root == r {
|
||||
self.path.push(merkle::PathNode::Left(*l))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct MMRFolds {
|
||||
folds: Vec<([u8; 32], [u8; 32])>,
|
||||
}
|
||||
|
||||
impl MMR {
|
||||
@ -159,6 +175,25 @@ impl MMR {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn folds(&self, elem: &[u8]) -> MMRFolds {
|
||||
let mut folds = MMRFolds::default();
|
||||
|
||||
let mut height = 1;
|
||||
let mut right = merkle::leaf(elem);
|
||||
|
||||
for i in (0..self.roots.len()).rev() {
|
||||
if self.roots[i].height == height {
|
||||
folds.folds.push((self.roots[i].root, right));
|
||||
right = merkle::node(self.roots[i].root, right);
|
||||
height += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
folds
|
||||
}
|
||||
|
||||
pub fn push(&mut self, elem: &[u8]) -> MMRProof {
|
||||
let new_root = Root {
|
||||
root: merkle::leaf(elem),
|
||||
@ -304,4 +339,29 @@ mod test {
|
||||
);
|
||||
assert!(mmr.verify_proof(b"!", &proof));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mmr_proof_update() {
|
||||
let mut mmr = MMR::new();
|
||||
|
||||
let mut proofs = vec![];
|
||||
|
||||
for x in 'a'..='z' {
|
||||
let b = [x as u8];
|
||||
proofs.push((b, mmr.push(&b), mmr.clone()));
|
||||
}
|
||||
|
||||
while !proofs.is_empty() {
|
||||
let (x, mut x_pf, mut x_mmr) = proofs.remove(0);
|
||||
assert!(x_mmr.verify_proof(&x, &x_pf));
|
||||
|
||||
for (y, _, y_mmr) in proofs.iter() {
|
||||
x_pf.update(&x, x_mmr.folds(y));
|
||||
assert!(y_mmr.verify_proof(&x, &x_pf));
|
||||
|
||||
x_mmr.push(y);
|
||||
assert_eq!(&x_mmr, y_mmr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,11 +2,22 @@ pub mod crust;
|
||||
pub mod ds;
|
||||
pub mod mantle;
|
||||
|
||||
pub type Hash = risc0_zkvm::sha::rust_crypto::Sha256;
|
||||
pub use digest::Digest;
|
||||
pub use risc0_zkvm::sha::rust_crypto::{Digest, Sha256};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
pub type Hash = Sha256;
|
||||
|
||||
pub fn hash(data: &[u8]) -> [u8; 32] {
|
||||
let mut hasher = Hash::new();
|
||||
hasher.update(data);
|
||||
hasher.finalize().into()
|
||||
}
|
||||
|
||||
// TODO: spec serializiation
|
||||
pub fn serialize(data: impl Serialize) -> Vec<u8> {
|
||||
bincode::serialize(&data).unwrap()
|
||||
}
|
||||
|
||||
pub fn deserialize<T: DeserializeOwned>(bytes: &[u8]) -> T {
|
||||
bincode::deserialize(bytes).unwrap()
|
||||
}
|
||||
|
||||
@ -1,20 +1,24 @@
|
||||
use crate::{
|
||||
crust::{NoteCommitment, Nullifier},
|
||||
ds::indexed::{BatchUpdateProof, NullifierTree},
|
||||
ds::mmr::{MMRProof, MMR},
|
||||
crust::{BundleRoot, NoteCommitment, Nullifier},
|
||||
ds::{
|
||||
indexed::{BatchUpdateProof, NullifierTree},
|
||||
mmr::{MMRProof, MMR},
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Ledger {
|
||||
cm_root: [u8; 32],
|
||||
nf_root: [u8; 32],
|
||||
pub cm_root: [u8; 32],
|
||||
pub nf_root: [u8; 32],
|
||||
pub bundles_root: [u8; 32],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct LedgerWitness {
|
||||
pub commitments: MMR,
|
||||
pub nf_root: [u8; 32],
|
||||
pub bundles: MMR,
|
||||
}
|
||||
|
||||
impl LedgerWitness {
|
||||
@ -22,6 +26,7 @@ impl LedgerWitness {
|
||||
Ledger {
|
||||
cm_root: self.commitments.commit(),
|
||||
nf_root: self.nf_root,
|
||||
bundles_root: self.bundles.commit(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +38,10 @@ impl LedgerWitness {
|
||||
self.commitments.push(&cm.0);
|
||||
}
|
||||
|
||||
pub fn add_bundle(&mut self, bundle_root: BundleRoot) {
|
||||
self.bundles.push(&bundle_root.0);
|
||||
}
|
||||
|
||||
pub fn assert_nfs_update(&mut self, nullifiers: &[Nullifier], proof: &BatchUpdateProof) {
|
||||
// update the nullifer root with the nullifier inserted into the tree
|
||||
self.nf_root = proof.verify(nullifiers, self.nf_root);
|
||||
@ -43,6 +52,7 @@ impl LedgerWitness {
|
||||
pub struct LedgerState {
|
||||
pub commitments: MMR,
|
||||
pub nullifiers: NullifierTree,
|
||||
pub bundles: MMR,
|
||||
}
|
||||
|
||||
impl LedgerState {
|
||||
@ -50,6 +60,7 @@ impl LedgerState {
|
||||
LedgerWitness {
|
||||
commitments: self.commitments.clone(),
|
||||
nf_root: self.nf_root(),
|
||||
bundles: self.bundles.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,4 +76,9 @@ impl LedgerState {
|
||||
pub fn add_nullifiers(&mut self, nfs: Vec<Nullifier>) -> BatchUpdateProof {
|
||||
self.nullifiers.insert_batch(nfs)
|
||||
}
|
||||
|
||||
pub fn add_bundle(&mut self, bundle_root: BundleRoot) -> (MMR, MMRProof) {
|
||||
let proof = self.bundles.push(&bundle_root.0);
|
||||
(self.bundles.clone(), proof)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,19 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# We generate in a *loop* because some risc0 proofs are recursive, so if a child
|
||||
# proof's id changes, then the parent proof will also change, but we don't see the
|
||||
# parent's id change until the next run.
|
||||
# Order of these proofs is important, we sequence them topologically by composition order
|
||||
proofs=$(cat <<EOF
|
||||
risc0_proofs
|
||||
bundle_risc0_proof
|
||||
ledger_risc0_proof
|
||||
EOF
|
||||
)
|
||||
|
||||
cargo run --bin gen_risc0_images > risc0_images/src/lib.rs.new
|
||||
for proof in $proofs; do
|
||||
echo "Building $proof"
|
||||
# Run the cargo risczero build command and process output line by line
|
||||
cargo risczero build --manifest-path "$proof/Cargo.toml" | while read -r line; do
|
||||
# Parse out the
|
||||
if [[ $line =~ ImageID:\ ([0-9a-f]+)\ -\ \"(.+)\" ]]; then
|
||||
image_id="${BASH_REMATCH[1]}"
|
||||
image_elf="${BASH_REMATCH[2]}"
|
||||
image_name=$(basename $image_elf | tr '[:lower:]' '[:upper:]')
|
||||
echo "${image_name}_ID: $image_id"
|
||||
echo "${image_name}_ELF: $(cat $image_elf | xxd -p | head -c 32)..."
|
||||
|
||||
while ! cmp -s risc0_images/src/lib.rs.new risc0_images/src/lib.rs
|
||||
do
|
||||
mv risc0_images/src/lib.rs.new risc0_images/src/lib.rs
|
||||
cargo run --bin gen_risc0_images > risc0_images/src/lib.rs.new
|
||||
echo "-------- FINISHED UPDATE ITERATION --------"
|
||||
echo $image_id | tr -d '\n' > "risc0_images/src/${image_name}_ID"
|
||||
cp $image_elf "risc0_images/src/${image_name}_ELF"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
rm risc0_images/src/lib.rs.new
|
||||
|
||||
cargo test -p risc0_images_police
|
||||
|
||||
@ -6,9 +6,10 @@ edition = "2021"
|
||||
[dependencies]
|
||||
cl = { path = "../cl" }
|
||||
ledger_proof_statements = { path = "../ledger_proof_statements" }
|
||||
risc0_images = { path = "../risc0_images" }
|
||||
risc0_images = { path = "../risc0_images", features = ["elf"]}
|
||||
risc0-zkvm = { version = "1.0", features = ["prove", "metal"] }
|
||||
risc0-groth16 = { version = "1.0" }
|
||||
rand = "0.8.5"
|
||||
rand_core = "0.6.0"
|
||||
thiserror = "1.0.62"
|
||||
hex = "0.4"
|
||||
@ -1,16 +1,22 @@
|
||||
use crate::tx::ProvedTx;
|
||||
use cl::crust::{Bundle, BundleWitness};
|
||||
|
||||
use hex::FromHex;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProvedBundle {
|
||||
pub risc0_receipt: risc0_zkvm::Receipt,
|
||||
}
|
||||
|
||||
impl ProvedBundle {
|
||||
pub fn prove(bundle: &BundleWitness, txs: Vec<ProvedTx>) -> Self {
|
||||
pub fn prove(txs: Vec<ProvedTx>) -> Self {
|
||||
//show that all ptx's are individually valid, and balance to 0
|
||||
let mut env = risc0_zkvm::ExecutorEnv::builder();
|
||||
|
||||
let bundle = BundleWitness {
|
||||
txs: txs.iter().map(|tx| tx.public()).collect(),
|
||||
};
|
||||
|
||||
for proved_tx in txs {
|
||||
env.add_assumption(proved_tx.risc0_receipt);
|
||||
}
|
||||
@ -23,11 +29,7 @@ impl ProvedBundle {
|
||||
|
||||
let opts = risc0_zkvm::ProverOpts::succinct();
|
||||
let prove_info = prover
|
||||
.prove_with_opts(
|
||||
env,
|
||||
risc0_images::nomos_mantle_bundle_risc0_proof::BUNDLE_ELF,
|
||||
&opts,
|
||||
)
|
||||
.prove_with_opts(env, risc0_images::BUNDLE_ELF, &opts)
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
@ -50,7 +52,7 @@ impl ProvedBundle {
|
||||
|
||||
pub fn verify(&self) -> bool {
|
||||
self.risc0_receipt
|
||||
.verify(risc0_images::nomos_mantle_bundle_risc0_proof::BUNDLE_ID)
|
||||
.verify(<[u8; 32]>::from_hex(risc0_images::BUNDLE_ID).unwrap())
|
||||
.is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,8 @@ use ledger_proof_statements::ledger::{LedgerBundleWitness, LedgerProofPrivate, L
|
||||
use crate::bundle::ProvedBundle;
|
||||
use cl::mantle::{ledger::LedgerState, zone::ZoneId};
|
||||
|
||||
use hex::FromHex;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProvedLedgerTransition {
|
||||
pub risc0_receipt: risc0_zkvm::Receipt,
|
||||
@ -21,29 +23,28 @@ impl ProvedLedgerTransition {
|
||||
|
||||
let bundle = proved_bundle.public();
|
||||
|
||||
let zone_ledger_update = bundle
|
||||
let zone_ledger_updates = bundle
|
||||
.updates
|
||||
.iter()
|
||||
.find(|update| update.zone_id == zone_id)
|
||||
.get(&zone_id)
|
||||
.expect("why are we proving this bundle for this zone if it's not involved?");
|
||||
|
||||
let cm_root_proofs =
|
||||
BTreeMap::from_iter(zone_ledger_update.frontier_nodes.iter().map(|root| {
|
||||
// We make the simplifying assumption that bundle proofs
|
||||
// are done w.r.t. the latest MMR (hence, empty merkle proofs)
|
||||
//
|
||||
// We can remove this assumption by tracking old MMR roots in the LedgerState
|
||||
(root.root, vec![])
|
||||
}));
|
||||
|
||||
nullifiers.extend(zone_ledger_update.inputs.clone());
|
||||
let mut cm_root_proofs = BTreeMap::new();
|
||||
for zone_ledger_update in zone_ledger_updates {
|
||||
cm_root_proofs.extend(
|
||||
zone_ledger_update
|
||||
.frontier_nodes
|
||||
.iter()
|
||||
.map(|root| (root.root, vec![])),
|
||||
);
|
||||
nullifiers.extend(zone_ledger_update.inputs.clone());
|
||||
}
|
||||
|
||||
let ledger_bundle = LedgerBundleWitness {
|
||||
bundle,
|
||||
cm_root_proofs,
|
||||
};
|
||||
|
||||
w_bundles.push(ledger_bundle)
|
||||
w_bundles.push(ledger_bundle);
|
||||
}
|
||||
|
||||
let witness = LedgerProofPrivate {
|
||||
@ -54,13 +55,19 @@ impl ProvedLedgerTransition {
|
||||
};
|
||||
|
||||
for bundle in &witness.bundles {
|
||||
for update in &bundle.bundle.updates {
|
||||
if update.zone_id == zone_id {
|
||||
for cm in &update.outputs {
|
||||
ledger.add_commitment(cm);
|
||||
}
|
||||
let updates = bundle
|
||||
.bundle
|
||||
.updates
|
||||
.get(&zone_id)
|
||||
.expect("should have a bundle from the zone we are proofing for");
|
||||
|
||||
for update in updates {
|
||||
for (cm, _data) in &update.outputs {
|
||||
ledger.add_commitment(cm);
|
||||
}
|
||||
}
|
||||
|
||||
ledger.add_bundle(bundle.bundle.root);
|
||||
}
|
||||
|
||||
witness.write(&mut env);
|
||||
@ -75,7 +82,7 @@ impl ProvedLedgerTransition {
|
||||
// This struct contains the receipt along with statistics about execution of the guest
|
||||
let opts = risc0_zkvm::ProverOpts::succinct();
|
||||
let prove_info = prover
|
||||
.prove_with_opts(env, risc0_images::ledger_validity_proof::LEDGER_ELF, &opts)
|
||||
.prove_with_opts(env, risc0_images::LEDGER_ELF, &opts)
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
@ -99,7 +106,7 @@ impl ProvedLedgerTransition {
|
||||
|
||||
pub fn verify(&self) -> bool {
|
||||
self.risc0_receipt
|
||||
.verify(risc0_images::ledger_validity_proof::LEDGER_ID)
|
||||
.verify(<[u8; 32]>::from_hex(risc0_images::LEDGER_ID).unwrap())
|
||||
.is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
use cl::mantle::zone::Stf;
|
||||
use ledger_proof_statements::stf::StfPublic;
|
||||
|
||||
use hex::FromHex;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StfProof {
|
||||
pub risc0_id: [u32; 8],
|
||||
pub risc0_id: [u8; 32],
|
||||
pub public: StfPublic,
|
||||
pub risc0_receipt: risc0_zkvm::Receipt,
|
||||
}
|
||||
@ -15,7 +17,7 @@ pub fn risc0_stf(risc0_id: [u32; 8]) -> Stf {
|
||||
}
|
||||
|
||||
impl StfProof {
|
||||
pub fn from_risc0(risc0_id: [u32; 8], risc0_receipt: risc0_zkvm::Receipt) -> Self {
|
||||
pub fn from_risc0(risc0_id: [u8; 32], risc0_receipt: risc0_zkvm::Receipt) -> Self {
|
||||
Self {
|
||||
risc0_id,
|
||||
public: risc0_receipt.journal.decode().unwrap(),
|
||||
@ -24,14 +26,14 @@ impl StfProof {
|
||||
}
|
||||
|
||||
pub fn stf(&self) -> Stf {
|
||||
risc0_stf(self.risc0_id)
|
||||
self.risc0_id
|
||||
}
|
||||
pub fn verify(&self) -> bool {
|
||||
self.risc0_receipt.verify(self.risc0_id).is_ok()
|
||||
}
|
||||
|
||||
pub fn nop_stf() -> [u8; 32] {
|
||||
risc0_stf(risc0_images::nomos_mantle_risc0_proofs::STF_NOP_ID)
|
||||
FromHex::from_hex(risc0_images::STF_NOP_ID).unwrap()
|
||||
}
|
||||
|
||||
pub fn prove_nop(public: StfPublic) -> Self {
|
||||
@ -47,11 +49,7 @@ impl StfProof {
|
||||
|
||||
let opts = risc0_zkvm::ProverOpts::succinct();
|
||||
let prove_info = prover
|
||||
.prove_with_opts(
|
||||
env,
|
||||
risc0_images::nomos_mantle_risc0_proofs::STF_NOP_ELF,
|
||||
&opts,
|
||||
)
|
||||
.prove_with_opts(env, risc0_images::STF_NOP_ELF, &opts)
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
@ -64,7 +62,7 @@ impl StfProof {
|
||||
let receipt = prove_info.receipt;
|
||||
|
||||
Self {
|
||||
risc0_id: risc0_images::nomos_mantle_risc0_proofs::STF_NOP_ID,
|
||||
risc0_id: FromHex::from_hex(risc0_images::STF_NOP_ID).unwrap(),
|
||||
public,
|
||||
risc0_receipt: receipt,
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ use crate::{
|
||||
error::{Error, Result},
|
||||
};
|
||||
use cl::crust::{Tx, TxWitness};
|
||||
use hex::FromHex;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProvedTx {
|
||||
@ -36,11 +37,7 @@ impl ProvedTx {
|
||||
// This struct contains the receipt along with statistics about execution of the guest
|
||||
let opts = risc0_zkvm::ProverOpts::succinct();
|
||||
let prove_info = prover
|
||||
.prove_with_opts(
|
||||
env,
|
||||
risc0_images::nomos_mantle_tx_risc0_proof::TX_ELF,
|
||||
&opts,
|
||||
)
|
||||
.prove_with_opts(env, risc0_images::TX_ELF, &opts)
|
||||
.map_err(|_| Error::Risc0ProofFailed)?;
|
||||
|
||||
println!(
|
||||
@ -61,7 +58,7 @@ impl ProvedTx {
|
||||
|
||||
pub fn verify(&self) -> bool {
|
||||
self.risc0_receipt
|
||||
.verify(risc0_images::nomos_mantle_tx_risc0_proof::TX_ID)
|
||||
.verify(<[u8; 32]>::from_hex(risc0_images::TX_ID).unwrap())
|
||||
.is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,15 +45,10 @@ impl ProvedBatchUpdate {
|
||||
.zip(self.stf_proofs.iter())
|
||||
.zip(self.ledger_proofs.iter())
|
||||
{
|
||||
if ledger_proof.public().old_ledger != update.old.ledger
|
||||
|| ledger_proof.public().ledger != update.new.ledger
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if stf_proof.public.old != update.old || stf_proof.public.new != update.new {
|
||||
return false;
|
||||
}
|
||||
assert_eq!(ledger_proof.public().old_ledger, update.old.ledger);
|
||||
assert_eq!(ledger_proof.public().ledger, update.new.ledger);
|
||||
assert_eq!(stf_proof.public.old, update.old);
|
||||
assert_eq!(stf_proof.public.new, update.new);
|
||||
}
|
||||
|
||||
true
|
||||
|
||||
@ -64,8 +64,8 @@ fn cross_transfer_transition(
|
||||
|
||||
let tx_witness = TxWitness::default()
|
||||
.add_input(input, input_proof)
|
||||
.add_output(transfer, vec![])
|
||||
.add_output(change, vec![]);
|
||||
.add_output(transfer, "")
|
||||
.add_output(change, "");
|
||||
|
||||
let proved_tx = ProvedTx::prove(
|
||||
tx_witness.clone(),
|
||||
@ -74,12 +74,7 @@ fn cross_transfer_transition(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let bundle = ProvedBundle::prove(
|
||||
&BundleWitness {
|
||||
txs: vec![proved_tx.public()],
|
||||
},
|
||||
vec![proved_tx],
|
||||
);
|
||||
let bundle = ProvedBundle::prove(vec![proved_tx]);
|
||||
|
||||
println!("proving ledger A transition");
|
||||
let ledger_in_transition =
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
[package]
|
||||
name = "stf_nop"
|
||||
name = "ledger_risc0_proof"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
cl = { path = "../../cl" }
|
||||
ledger_proof_statements = { path = "../../ledger_proof_statements" }
|
||||
|
||||
cl = { path = "../cl" }
|
||||
ledger_proof_statements = { path = "../ledger_proof_statements" }
|
||||
risc0_images = { path = "../risc0_images" }
|
||||
hex = "0.4"
|
||||
|
||||
[patch.crates-io]
|
||||
# add RISC Zero accelerator support for all downstream usages of the following crates.
|
||||
@ -1,4 +1,5 @@
|
||||
use cl::ds::merkle;
|
||||
use hex::FromHex;
|
||||
use ledger_proof_statements::ledger::{
|
||||
LedgerBundleWitness, LedgerProofPrivate, LedgerProofPublic, SyncLog,
|
||||
};
|
||||
@ -23,42 +24,46 @@ fn main() {
|
||||
} in bundles
|
||||
{
|
||||
env::verify(
|
||||
risc0_images::nomos_mantle_bundle_risc0_proof::BUNDLE_ID,
|
||||
<[u8; 32]>::from_hex(risc0_images::BUNDLE_ID).unwrap(),
|
||||
&serde::to_vec(&bundle).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let zones = Vec::from_iter(bundle.updates.iter().map(|update| update.zone_id));
|
||||
if !(zones.len() == 1 && zones[0] == id) {
|
||||
if bundle.updates.len() > 1 {
|
||||
// This is a cross zone bundle, add a sync log for it to ensure all zones
|
||||
// also approve it.
|
||||
sync_logs.push(SyncLog {
|
||||
bundle: bundle.root,
|
||||
zones,
|
||||
zones: bundle.updates.keys().copied().collect(),
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(ledger_update) = bundle
|
||||
let ledger_updates = bundle
|
||||
.updates
|
||||
.into_iter()
|
||||
.filter(|update| update.zone_id == id)
|
||||
.next()
|
||||
{
|
||||
.get(&id)
|
||||
.expect("attempting to prove a bundle that is not for this zone");
|
||||
|
||||
for ledger_update in ledger_updates {
|
||||
for node in &ledger_update.frontier_nodes {
|
||||
let past_cm_root_proof = cm_root_proofs
|
||||
.get(&node.root)
|
||||
.expect("missing cm root proof");
|
||||
|
||||
let expected_current_cm_root = merkle::path_root(node.root, past_cm_root_proof);
|
||||
assert!(old_ledger.valid_cm_root(expected_current_cm_root))
|
||||
assert!(
|
||||
old_ledger.valid_cm_root(expected_current_cm_root)
|
||||
|| ledger.valid_cm_root(expected_current_cm_root)
|
||||
);
|
||||
}
|
||||
|
||||
for cm in &ledger_update.outputs {
|
||||
for (cm, _data) in &ledger_update.outputs {
|
||||
ledger.add_commitment(cm);
|
||||
outputs.push(*cm)
|
||||
outputs.push(*cm);
|
||||
}
|
||||
|
||||
nullifiers.extend(ledger_update.inputs);
|
||||
nullifiers.extend(ledger_update.inputs.clone());
|
||||
}
|
||||
|
||||
ledger.add_bundle(bundle.root);
|
||||
}
|
||||
|
||||
// TODO: sort outside and check
|
||||
@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "ledger_validity_proof"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[build-dependencies]
|
||||
risc0-build = { version = "1.0" }
|
||||
|
||||
[package.metadata.risc0]
|
||||
methods = [ "ledger"]
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
fn main() {
|
||||
risc0_build::embed_methods();
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
[package]
|
||||
name = "ledger"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
cl = { path = "../../cl" }
|
||||
ledger_proof_statements = { path = "../../ledger_proof_statements" }
|
||||
risc0_images = { path = "../../risc0_images" }
|
||||
|
||||
[patch.crates-io]
|
||||
# add RISC Zero accelerator support for all downstream usages of the following crates.
|
||||
sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.8-risczero.0" }
|
||||
crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.5-risczero.0" }
|
||||
curve25519-dalek = { git = "https://github.com/risc0/curve25519-dalek", tag = "curve25519-4.1.2-risczero.0" }
|
||||
@ -1 +0,0 @@
|
||||
include!(concat!(env!("OUT_DIR"), "/methods.rs"));
|
||||
@ -5,3 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
binary_macros = "1.0.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
elf = []
|
||||
BIN
emmarin/cl/risc0_images/src/BUNDLE_ELF
Executable file
BIN
emmarin/cl/risc0_images/src/BUNDLE_ELF
Executable file
Binary file not shown.
1
emmarin/cl/risc0_images/src/BUNDLE_ID
Normal file
1
emmarin/cl/risc0_images/src/BUNDLE_ID
Normal file
@ -0,0 +1 @@
|
||||
2aceb7181ab1155497eaf932c71dfe8bd9fcab476c543d7e03163fe083b15e19
|
||||
BIN
emmarin/cl/risc0_images/src/LEDGER_ELF
Executable file
BIN
emmarin/cl/risc0_images/src/LEDGER_ELF
Executable file
Binary file not shown.
1
emmarin/cl/risc0_images/src/LEDGER_ID
Normal file
1
emmarin/cl/risc0_images/src/LEDGER_ID
Normal file
@ -0,0 +1 @@
|
||||
4e5e4fdaa2448d4bb16aecf2b0c6279ef7a573df3ccb999201a1f1b7400f8732
|
||||
BIN
emmarin/cl/risc0_images/src/STF_NOP_ELF
Executable file
BIN
emmarin/cl/risc0_images/src/STF_NOP_ELF
Executable file
Binary file not shown.
1
emmarin/cl/risc0_images/src/STF_NOP_ID
Normal file
1
emmarin/cl/risc0_images/src/STF_NOP_ID
Normal file
@ -0,0 +1 @@
|
||||
557a0804bbf1220e66f4a695c24d57de02cb9fd52d646cb99dd43572561a6807
|
||||
BIN
emmarin/cl/risc0_images/src/TX_ELF
Executable file
BIN
emmarin/cl/risc0_images/src/TX_ELF
Executable file
Binary file not shown.
1
emmarin/cl/risc0_images/src/TX_ID
Normal file
1
emmarin/cl/risc0_images/src/TX_ID
Normal file
@ -0,0 +1 @@
|
||||
70c7de2aaee012e499f1a7c659d84ce35acadf59c8714931fd1e60cc7ef28067
|
||||
File diff suppressed because one or more lines are too long
@ -1,14 +0,0 @@
|
||||
[package]
|
||||
name = "risc0_images_police"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
[dependencies]
|
||||
sha2 = "0.10"
|
||||
base64 = "0.22"
|
||||
risc0_images = { path = "../risc0_images" }
|
||||
nomos_mantle_risc0_proofs = { path = "../risc0_proofs" }
|
||||
nomos_mantle_bundle_risc0_proof = { path = "../bundle_risc0_proof" }
|
||||
nomos_mantle_tx_risc0_proof = { path = "../tx_risc0_proof" }
|
||||
ledger_validity_proof = { path = "../ledger_validity_proof" }
|
||||
@ -1,25 +0,0 @@
|
||||
use base64::prelude::*;
|
||||
|
||||
macro_rules! gen_risc0_image {
|
||||
($module:ident, $id:ident, $elf:ident) => {
|
||||
println!("pub mod {} {{", stringify!($module));
|
||||
println!(
|
||||
" pub const {}: [u32; 8] = {:?};",
|
||||
stringify!($id),
|
||||
$module::$id
|
||||
);
|
||||
println!(
|
||||
" pub static {}: &[u8] = binary_macros::base64!({:?});",
|
||||
stringify!($elf),
|
||||
BASE64_STANDARD.encode(&$module::$elf)
|
||||
);
|
||||
println!("}}");
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
gen_risc0_image!(nomos_mantle_risc0_proofs, STF_NOP_ID, STF_NOP_ELF);
|
||||
gen_risc0_image!(nomos_mantle_bundle_risc0_proof, BUNDLE_ID, BUNDLE_ELF);
|
||||
gen_risc0_image!(nomos_mantle_tx_risc0_proof, TX_ID, TX_ELF);
|
||||
gen_risc0_image!(ledger_validity_proof, LEDGER_ID, LEDGER_ELF);
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
fn hash(x: impl AsRef<[u8]>) -> [u8; 32] {
|
||||
use sha2::{Digest, Sha256};
|
||||
Sha256::digest(x).into()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ensure_images_are_correct() {
|
||||
assert_eq!(
|
||||
risc0_images::nomos_mantle_risc0_proofs::STF_NOP_ID,
|
||||
nomos_mantle_risc0_proofs::STF_NOP_ID,
|
||||
"STF_NOP_ID"
|
||||
);
|
||||
assert_eq!(
|
||||
hash(risc0_images::nomos_mantle_risc0_proofs::STF_NOP_ELF),
|
||||
hash(nomos_mantle_risc0_proofs::STF_NOP_ELF),
|
||||
"STF_NOP_ELF"
|
||||
);
|
||||
assert_eq!(
|
||||
risc0_images::nomos_mantle_bundle_risc0_proof::BUNDLE_ID,
|
||||
nomos_mantle_bundle_risc0_proof::BUNDLE_ID,
|
||||
"BUNDLE_ID"
|
||||
);
|
||||
assert_eq!(
|
||||
hash(risc0_images::nomos_mantle_bundle_risc0_proof::BUNDLE_ELF),
|
||||
hash(nomos_mantle_bundle_risc0_proof::BUNDLE_ELF),
|
||||
"BUNDLE_ELF"
|
||||
);
|
||||
assert_eq!(
|
||||
risc0_images::nomos_mantle_tx_risc0_proof::TX_ID,
|
||||
nomos_mantle_tx_risc0_proof::TX_ID,
|
||||
"TX_ID"
|
||||
);
|
||||
assert_eq!(
|
||||
hash(risc0_images::nomos_mantle_tx_risc0_proof::TX_ELF),
|
||||
hash(nomos_mantle_tx_risc0_proof::TX_ELF),
|
||||
"TX_ELF"
|
||||
);
|
||||
assert_eq!(
|
||||
risc0_images::ledger_validity_proof::LEDGER_ID,
|
||||
ledger_validity_proof::LEDGER_ID,
|
||||
"LEDGER_ID"
|
||||
);
|
||||
assert_eq!(
|
||||
hash(risc0_images::ledger_validity_proof::LEDGER_ELF),
|
||||
hash(ledger_validity_proof::LEDGER_ELF),
|
||||
"LEDGER_ELF"
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,17 @@
|
||||
[package]
|
||||
name = "nomos_mantle_risc0_proofs"
|
||||
name = "risc0_proofs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[build-dependencies]
|
||||
risc0-build = { version = "1.0" }
|
||||
[dependencies]
|
||||
risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
cl = { path = "../cl" }
|
||||
ledger_proof_statements = { path = "../ledger_proof_statements" }
|
||||
|
||||
[package.metadata.risc0]
|
||||
methods = ["stf_nop"]
|
||||
|
||||
[patch.crates-io]
|
||||
# add RISC Zero accelerator support for all downstream usages of the following crates.
|
||||
sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.8-risczero.0" }
|
||||
crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.5-risczero.0" }
|
||||
curve25519-dalek = { git = "https://github.com/risc0/curve25519-dalek", tag = "curve25519-4.1.2-risczero.0" }
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
fn main() {
|
||||
risc0_build::embed_methods();
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
include!(concat!(env!("OUT_DIR"), "/methods.rs"));
|
||||
@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "nomos_mantle_tx_risc0_proof"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[build-dependencies]
|
||||
risc0-build = { version = "1.0" }
|
||||
|
||||
[package.metadata.risc0]
|
||||
methods = ["tx"]
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
fn main() {
|
||||
risc0_build::embed_methods();
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
include!(concat!(env!("OUT_DIR"), "/methods.rs"));
|
||||
@ -1,19 +0,0 @@
|
||||
[package]
|
||||
name = "tx"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
cl = { path = "../../cl" }
|
||||
ledger_proof_statements = { path = "../../ledger_proof_statements" }
|
||||
|
||||
|
||||
[patch.crates-io]
|
||||
# add RISC Zero accelerator support for all downstream usages of the following crates.
|
||||
sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.8-risczero.0" }
|
||||
crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.5-risczero.0" }
|
||||
curve25519-dalek = { git = "https://github.com/risc0/curve25519-dalek", tag = "curve25519-4.1.2-risczero.0" }
|
||||
Loading…
x
Reference in New Issue
Block a user