Merge pull request #45 from logos-co/drusu/sparse_merkle_tree_nullifiers
PACT: Sparse Merkle Tree & Rework proof recusion
This commit is contained in:
commit
edec3632f1
|
@ -0,0 +1 @@
|
|||
*profile.pb
|
|
@ -1,6 +1,14 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
members = [ "cl", "ledger", "ledger_proof_statements", "risc0_proofs", "ledger_validity_proof"]
|
||||
members = [
|
||||
"cl",
|
||||
"ledger",
|
||||
"ledger_proof_statements",
|
||||
"risc0_proofs",
|
||||
"bundle_risc0_proof",
|
||||
"ptx_risc0_proof",
|
||||
"ledger_validity_proof"
|
||||
]
|
||||
|
||||
# Always optimize; building and running the risc0_proofs takes much longer without optimization.
|
||||
[profile.dev]
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "nomos_cl_bundle_risc0_proof"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[build-dependencies]
|
||||
risc0-build = { version = "1.0" }
|
||||
|
||||
[package.metadata.risc0]
|
||||
methods = ["bundle"]
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
risc0_build::embed_methods();
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "balance"
|
||||
name = "bundle"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
@ -10,6 +10,7 @@ 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" }
|
||||
nomos_cl_ptx_risc0_proof = { path = "../../ptx_risc0_proof" }
|
||||
|
||||
|
||||
[patch.crates-io]
|
|
@ -0,0 +1,49 @@
|
|||
use cl::cl::BalanceWitness;
|
||||
use cl::zone_layer::notes::ZoneId;
|
||||
use ledger_proof_statements::bundle::{BundlePrivate, BundlePublic, LedgerUpdate};
|
||||
use risc0_zkvm::{guest::env, serde};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
fn main() {
|
||||
let bundle_private: BundlePrivate = env::read();
|
||||
let bundle_id = bundle_private.id();
|
||||
|
||||
let BundlePrivate { bundle, balances } = bundle_private;
|
||||
assert_eq!(bundle.len(), balances.len());
|
||||
|
||||
let mut zone_ledger_updates: BTreeMap<ZoneId, LedgerUpdate> = BTreeMap::new();
|
||||
|
||||
for (ptx_public, balance) in bundle.into_iter().zip(balances.iter()) {
|
||||
assert_eq!(ptx_public.ptx.balance, balance.commit());
|
||||
env::verify(
|
||||
nomos_cl_ptx_risc0_proof::PTX_ID,
|
||||
&serde::to_vec(&ptx_public).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for (input, cm_mmr) in ptx_public.ptx.inputs.iter().zip(ptx_public.cm_mmrs) {
|
||||
let zone_ledger_update = zone_ledger_updates.entry(input.zone_id).or_default();
|
||||
|
||||
zone_ledger_update.nullifiers.push(input.nullifier);
|
||||
|
||||
zone_ledger_update
|
||||
.cm_roots
|
||||
.extend(cm_mmr.roots.iter().map(|r| r.root));
|
||||
}
|
||||
|
||||
for output in &ptx_public.ptx.outputs {
|
||||
zone_ledger_updates
|
||||
.entry(output.zone_id)
|
||||
.or_default()
|
||||
.commitments
|
||||
.push(output.note_comm);
|
||||
}
|
||||
}
|
||||
|
||||
assert!(BalanceWitness::combine(balances, [0u8; 16]).is_zero());
|
||||
|
||||
env::commit(&BundlePublic {
|
||||
bundle_id,
|
||||
zone_ledger_updates,
|
||||
});
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
include!(concat!(env!("OUT_DIR"), "/methods.rs"));
|
|
@ -13,3 +13,4 @@ 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"
|
|
@ -1,138 +0,0 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{cl::partial_tx::PartialTx, zone_layer::notes::ZoneId};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::collections::HashSet;
|
||||
|
||||
/// The transaction bundle is a collection of partial transactions.
|
||||
/// The goal in bundling transactions is to produce a set of partial transactions
|
||||
/// that balance each other.
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct BundleId(pub [u8; 32]);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Bundle {
|
||||
pub partials: Vec<PartialTx>,
|
||||
}
|
||||
|
||||
impl Bundle {
|
||||
pub fn zones(&self) -> HashSet<ZoneId> {
|
||||
self.partials
|
||||
.iter()
|
||||
.flat_map(|ptx| {
|
||||
ptx.inputs
|
||||
.iter()
|
||||
.map(|i| i.zone_id)
|
||||
.chain(ptx.outputs.iter().map(|o| o.zone_id))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
///
|
||||
pub fn id(&self) -> BundleId {
|
||||
// TODO: change to merkle root
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(b"NOMOS_CL_BUNDLE_ID");
|
||||
for ptx in &self.partials {
|
||||
hasher.update(&ptx.root().0);
|
||||
}
|
||||
|
||||
BundleId(hasher.finalize().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::cl::{
|
||||
balance::{BalanceWitness, UnitBalance},
|
||||
input::InputWitness,
|
||||
note::{derive_unit, NoteWitness},
|
||||
nullifier::NullifierSecret,
|
||||
output::OutputWitness,
|
||||
partial_tx::PartialTxWitness,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_bundle_balance() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let zone_id = [0; 32];
|
||||
let (nmo, eth, crv) = (derive_unit("NMO"), derive_unit("ETH"), derive_unit("CRV"));
|
||||
|
||||
let nf_a = NullifierSecret::random(&mut rng);
|
||||
let nf_b = NullifierSecret::random(&mut rng);
|
||||
let nf_c = NullifierSecret::random(&mut rng);
|
||||
|
||||
let nmo_10_utxo = OutputWitness::new(
|
||||
NoteWitness::basic(10, nmo, &mut rng),
|
||||
nf_a.commit(),
|
||||
zone_id,
|
||||
);
|
||||
let nmo_10_in = InputWitness::from_output(nmo_10_utxo, nf_a);
|
||||
|
||||
let eth_23_utxo = OutputWitness::new(
|
||||
NoteWitness::basic(23, eth, &mut rng),
|
||||
nf_b.commit(),
|
||||
zone_id,
|
||||
);
|
||||
let eth_23_in = InputWitness::from_output(eth_23_utxo, nf_b);
|
||||
|
||||
let crv_4840_out = OutputWitness::new(
|
||||
NoteWitness::basic(4840, crv, &mut rng),
|
||||
nf_c.commit(),
|
||||
zone_id,
|
||||
);
|
||||
|
||||
let ptx_unbalanced = PartialTxWitness {
|
||||
inputs: vec![nmo_10_in, eth_23_in],
|
||||
outputs: vec![crv_4840_out],
|
||||
balance_blinding: BalanceWitness::random_blinding(&mut rng),
|
||||
};
|
||||
|
||||
assert!(!ptx_unbalanced.balance().is_zero());
|
||||
assert_eq!(
|
||||
ptx_unbalanced.balance().balances,
|
||||
vec![
|
||||
UnitBalance {
|
||||
unit: nmo,
|
||||
pos: 0,
|
||||
neg: 10
|
||||
},
|
||||
UnitBalance {
|
||||
unit: eth,
|
||||
pos: 0,
|
||||
neg: 23
|
||||
},
|
||||
UnitBalance {
|
||||
unit: crv,
|
||||
pos: 4840,
|
||||
neg: 0
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
let crv_4840_in = InputWitness::from_output(crv_4840_out, nf_c);
|
||||
let nmo_10_out = OutputWitness::new(
|
||||
NoteWitness::basic(10, nmo, &mut rng),
|
||||
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
|
||||
zone_id,
|
||||
);
|
||||
let eth_23_out = OutputWitness::new(
|
||||
NoteWitness::basic(23, eth, &mut rng),
|
||||
NullifierSecret::random(&mut rng).commit(), // transferring to a random owner
|
||||
zone_id,
|
||||
);
|
||||
|
||||
let ptx_solved = PartialTxWitness {
|
||||
inputs: vec![crv_4840_in],
|
||||
outputs: vec![nmo_10_out, eth_23_out],
|
||||
balance_blinding: BalanceWitness::random_blinding(&mut rng),
|
||||
};
|
||||
|
||||
let bundle_balance =
|
||||
BalanceWitness::combine([ptx_unbalanced.balance(), ptx_solved.balance()], [0; 16]);
|
||||
|
||||
assert!(bundle_balance.is_zero());
|
||||
assert_eq!(bundle_balance.balances, vec![]);
|
||||
}
|
||||
}
|
|
@ -43,6 +43,8 @@ pub fn root<const N: usize>(elements: [[u8; 32]; N]) -> [u8; 32] {
|
|||
nodes[0]
|
||||
}
|
||||
|
||||
pub type Path = Vec<PathNode>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum PathNode {
|
||||
Left([u8; 32]),
|
||||
|
@ -66,7 +68,7 @@ pub fn path_root(leaf: [u8; 32], path: &[PathNode]) -> [u8; 32] {
|
|||
computed_hash
|
||||
}
|
||||
|
||||
pub fn path<const N: usize>(leaves: [[u8; 32]; N], idx: usize) -> Vec<PathNode> {
|
||||
pub fn path<const N: usize>(leaves: [[u8; 32]; N], idx: usize) -> Path {
|
||||
assert!(N.is_power_of_two());
|
||||
assert!(idx < N);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::cl::merkle;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
pub struct MMR {
|
||||
pub roots: Vec<Root>,
|
||||
}
|
||||
|
@ -18,9 +18,16 @@ pub struct MMRProof {
|
|||
pub path: Vec<merkle::PathNode>,
|
||||
}
|
||||
|
||||
impl MMRProof {
|
||||
pub fn root(&self, elem: &[u8]) -> [u8; 32] {
|
||||
let leaf = merkle::leaf(elem);
|
||||
merkle::path_root(leaf, &self.path)
|
||||
}
|
||||
}
|
||||
|
||||
impl MMR {
|
||||
pub fn new() -> Self {
|
||||
Self { roots: vec![] }
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn push(&mut self, elem: &[u8]) -> MMRProof {
|
||||
|
@ -52,8 +59,7 @@ impl MMR {
|
|||
|
||||
pub fn verify_proof(&self, elem: &[u8], proof: &MMRProof) -> bool {
|
||||
let path_len = proof.path.len();
|
||||
let leaf = merkle::leaf(elem);
|
||||
let root = merkle::path_root(leaf, &proof.path);
|
||||
let root = proof.root(elem);
|
||||
|
||||
for mmr_root in self.roots.iter() {
|
||||
if mmr_root.height == (path_len + 1) as u8 {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
pub mod balance;
|
||||
pub mod bundle;
|
||||
pub mod crypto;
|
||||
pub mod error;
|
||||
pub mod input;
|
||||
|
@ -9,9 +8,9 @@ pub mod note;
|
|||
pub mod nullifier;
|
||||
pub mod output;
|
||||
pub mod partial_tx;
|
||||
pub mod sparse_merkle;
|
||||
|
||||
pub use balance::{Balance, BalanceWitness};
|
||||
pub use bundle::Bundle;
|
||||
pub use input::{Input, InputWitness};
|
||||
pub use note::{Constraint, Nonce, NoteCommitment, NoteWitness};
|
||||
pub use nullifier::{Nullifier, NullifierCommitment, NullifierSecret};
|
||||
|
|
|
@ -25,7 +25,7 @@ pub struct NullifierCommitment([u8; 32]);
|
|||
// The nullifier attached to input notes to prove an input has not
|
||||
// already been spent.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct Nullifier([u8; 32]);
|
||||
pub struct Nullifier(pub [u8; 32]);
|
||||
|
||||
impl NullifierSecret {
|
||||
pub fn random(mut rng: impl RngCore) -> Self {
|
||||
|
|
|
@ -0,0 +1,360 @@
|
|||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::cl::merkle;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
/// absence of element is marked with all 0's
|
||||
pub static ABSENT: [u8; 32] = [0u8; 32];
|
||||
|
||||
/// presence of element is marked with all 1's
|
||||
pub static PRESENT: [u8; 32] = [255u8; 32];
|
||||
|
||||
lazy_static! {
|
||||
// the roots of empty merkle trees of diffent heights
|
||||
// i.e. all leafs are ABSENT
|
||||
static ref EMPTY_ROOTS: [[u8; 32]; 257] = {
|
||||
let mut roots = [ABSENT; 257];
|
||||
for h in 1..257 {
|
||||
roots[h] = merkle::node(roots[h - 1], roots[h - 1]);
|
||||
}
|
||||
|
||||
roots
|
||||
};
|
||||
}
|
||||
|
||||
pub fn sparse_root(elems: &BTreeSet<[u8; 32]>) -> [u8; 32] {
|
||||
sparse_root_rec(0, elems)
|
||||
}
|
||||
|
||||
fn sparse_root_rec(prefix: u64, elems: &BTreeSet<[u8; 32]>) -> [u8; 32] {
|
||||
if elems.is_empty() {
|
||||
return empty_tree_root(256 - prefix);
|
||||
}
|
||||
if prefix == 256 {
|
||||
assert_eq!(elems.len(), 1);
|
||||
return PRESENT;
|
||||
}
|
||||
// partition the elements
|
||||
let (left, right): (BTreeSet<_>, BTreeSet<_>) =
|
||||
elems.iter().partition(|e| !bit(prefix as u8, **e));
|
||||
|
||||
merkle::node(
|
||||
sparse_root_rec(prefix + 1, &left),
|
||||
sparse_root_rec(prefix + 1, &right),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn sparse_path(elem: [u8; 32], elems: &BTreeSet<[u8; 32]>) -> Vec<merkle::PathNode> {
|
||||
fn sparse_path_rec(
|
||||
prefix: u64,
|
||||
elem: [u8; 32],
|
||||
elems: &BTreeSet<[u8; 32]>,
|
||||
) -> Vec<merkle::PathNode> {
|
||||
if prefix == 256 {
|
||||
return Vec::new();
|
||||
}
|
||||
// partition the elements
|
||||
let (left, right): (BTreeSet<_>, BTreeSet<_>) =
|
||||
elems.iter().partition(|e| !bit(prefix as u8, **e));
|
||||
|
||||
match bit(prefix as u8, elem) {
|
||||
true => {
|
||||
let left_root = sparse_root_rec(prefix + 1, &left);
|
||||
let mut path = sparse_path_rec(prefix + 1, elem, &right);
|
||||
|
||||
path.push(merkle::PathNode::Left(left_root));
|
||||
path
|
||||
}
|
||||
false => {
|
||||
let right_root = sparse_root_rec(prefix + 1, &right);
|
||||
let mut path = sparse_path_rec(prefix + 1, elem, &left);
|
||||
|
||||
path.push(merkle::PathNode::Right(right_root));
|
||||
path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sparse_path_rec(0, elem, elems)
|
||||
}
|
||||
|
||||
pub fn path_key(path: &[merkle::PathNode]) -> [u8; 32] {
|
||||
assert_eq!(path.len(), 256);
|
||||
|
||||
let mut key = [0u8; 32];
|
||||
for byte_i in (0..32).rev() {
|
||||
let mut byte = 0u8;
|
||||
for bit_i in 0..8 {
|
||||
byte <<= 1;
|
||||
match path[byte_i * 8 + bit_i] {
|
||||
merkle::PathNode::Left(_) => byte += 1,
|
||||
merkle::PathNode::Right(_) => byte += 0,
|
||||
};
|
||||
}
|
||||
key[31 - byte_i] = byte;
|
||||
}
|
||||
|
||||
key
|
||||
}
|
||||
|
||||
fn empty_tree_root(height: u64) -> [u8; 32] {
|
||||
assert!(height <= 256);
|
||||
EMPTY_ROOTS[height as usize]
|
||||
}
|
||||
|
||||
fn bit(idx: u8, elem: [u8; 32]) -> bool {
|
||||
let byte = idx / 8;
|
||||
let bit_in_byte = idx - byte * 8;
|
||||
|
||||
(elem[byte as usize] & (1 << bit_in_byte)) != 0
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn random_hash() -> [u8; 32] {
|
||||
rand::random()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_neighbour_paths() {
|
||||
let elems = BTreeSet::from_iter([[0u8; 32]]);
|
||||
|
||||
let path_0 = sparse_path([0u8; 32], &elems);
|
||||
let mut key_1 = [0u8; 32];
|
||||
key_1[31] = 128;
|
||||
let path_1 = sparse_path(key_1, &elems);
|
||||
|
||||
assert_ne!(path_0, path_1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_bit_agreement() {
|
||||
fn path_bit(idx: u8, path: &[merkle::PathNode]) -> bool {
|
||||
match path[255 - idx as usize] {
|
||||
merkle::PathNode::Left(_) => true,
|
||||
merkle::PathNode::Right(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
let key = random_hash();
|
||||
let path = sparse_path(key, &BTreeSet::new());
|
||||
|
||||
for i in 0..=255 {
|
||||
let b = bit(i, key);
|
||||
let pb = path_bit(i, &path);
|
||||
assert_eq!(b, pb, "{}!={}@{}", b, pb, i);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_key() {
|
||||
let elems = BTreeSet::from_iter(std::iter::repeat_with(random_hash).take(10));
|
||||
|
||||
// membership proofs
|
||||
for e in elems.iter() {
|
||||
let path = sparse_path(*e, &elems);
|
||||
assert_eq!(path_key(&path), *e);
|
||||
}
|
||||
|
||||
// non-membership proofs
|
||||
for _ in 0..10 {
|
||||
let elem = random_hash();
|
||||
let path = sparse_path(elem, &elems);
|
||||
assert_eq!(path_key(&path), elem);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sparse_path() {
|
||||
let elems = BTreeSet::from_iter(std::iter::repeat_with(random_hash).take(10));
|
||||
|
||||
let root = sparse_root(&elems);
|
||||
|
||||
// membership proofs
|
||||
for e in elems.iter() {
|
||||
let path = sparse_path(*e, &elems);
|
||||
assert_eq!(merkle::path_root(PRESENT, &path), root);
|
||||
}
|
||||
|
||||
// non-membership proofs
|
||||
for _ in 0..10 {
|
||||
let elem = random_hash();
|
||||
let path = sparse_path(elem, &elems);
|
||||
assert!(!elems.contains(&elem));
|
||||
assert_eq!(merkle::path_root(ABSENT, &path), root);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sparse_non_membership_in_empty_tree() {
|
||||
let root = sparse_root(&BTreeSet::new());
|
||||
|
||||
let path = sparse_path([0u8; 32], &BTreeSet::new());
|
||||
|
||||
assert_eq!(merkle::path_root(ABSENT, &path), root);
|
||||
|
||||
for (h, node) in path.into_iter().enumerate() {
|
||||
match node {
|
||||
merkle::PathNode::Left(hash) | merkle::PathNode::Right(hash) => {
|
||||
assert_eq!(hash, empty_tree_root(h as u64))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sparse_root_left_most_occupied() {
|
||||
let root = sparse_root(&BTreeSet::from_iter([[0u8; 32]]));
|
||||
|
||||
// We are constructing the tree:
|
||||
//
|
||||
// / \
|
||||
// / \ 0 subtree
|
||||
// / \ 0 subtree
|
||||
// 1 0
|
||||
let mut expected_root = PRESENT;
|
||||
for h in 0..=255 {
|
||||
expected_root = merkle::node(expected_root, empty_tree_root(h))
|
||||
}
|
||||
|
||||
assert_eq!(root, expected_root)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sparse_root_right_most_occupied() {
|
||||
let root = sparse_root(&BTreeSet::from_iter([[255u8; 32]]));
|
||||
|
||||
// We are constructing the tree:
|
||||
//
|
||||
// /\
|
||||
// 0 /\
|
||||
// 0 /\
|
||||
// 0 1
|
||||
let mut expected_root = PRESENT;
|
||||
for h in 0..=255 {
|
||||
expected_root = merkle::node(empty_tree_root(h), expected_root)
|
||||
}
|
||||
|
||||
assert_eq!(root, expected_root)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sparse_root_middle_elem() {
|
||||
let elem = {
|
||||
let mut x = [255u8; 32];
|
||||
x[0] = 254;
|
||||
x
|
||||
};
|
||||
assert!(!bit(0, elem));
|
||||
for i in 1..=255 {
|
||||
assert!(bit(i, elem));
|
||||
}
|
||||
|
||||
let root = sparse_root(&BTreeSet::from_iter([elem]));
|
||||
|
||||
// We are constructing the tree:
|
||||
// root
|
||||
// / \
|
||||
// /\ 0
|
||||
// 0 /\
|
||||
// 0 /\
|
||||
// 0 ...
|
||||
// \
|
||||
// 1
|
||||
let mut expected_root = PRESENT;
|
||||
for h in 0..=254 {
|
||||
expected_root = merkle::node(empty_tree_root(h), expected_root)
|
||||
}
|
||||
expected_root = merkle::node(expected_root, empty_tree_root(255));
|
||||
|
||||
assert_eq!(root, expected_root)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sparse_root_middle_weave_elem() {
|
||||
let elem = [85u8; 32];
|
||||
for i in 0..=255 {
|
||||
assert_eq!(bit(i, elem), i % 2 == 0);
|
||||
}
|
||||
|
||||
let root = sparse_root(&BTreeSet::from_iter([elem]));
|
||||
|
||||
// We are constructing the tree:
|
||||
// /\
|
||||
// 0 /\
|
||||
// /\0
|
||||
// /\
|
||||
// 0 /\
|
||||
// /\0
|
||||
// 0 1
|
||||
|
||||
let mut expected_root = PRESENT;
|
||||
for h in 0..=255 {
|
||||
if h % 2 == 0 {
|
||||
expected_root = merkle::node(expected_root, empty_tree_root(h))
|
||||
} else {
|
||||
expected_root = merkle::node(empty_tree_root(h), expected_root)
|
||||
}
|
||||
}
|
||||
assert_eq!(root, expected_root)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sparse_multiple_elems() {
|
||||
let root = sparse_root(&BTreeSet::from_iter([[0u8; 32], [255u8; 32]]));
|
||||
|
||||
// We are constructing the tree:
|
||||
// root
|
||||
// / \
|
||||
// /\ /\
|
||||
// /\0 0 /\
|
||||
// 1 0 0 1
|
||||
|
||||
let mut left_root = PRESENT;
|
||||
for h in 0..=254 {
|
||||
left_root = merkle::node(left_root, empty_tree_root(h))
|
||||
}
|
||||
|
||||
let mut right_root = PRESENT;
|
||||
for h in 0..=254 {
|
||||
right_root = merkle::node(empty_tree_root(h), right_root)
|
||||
}
|
||||
let expected_root = merkle::node(left_root, right_root);
|
||||
|
||||
assert_eq!(root, expected_root)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bit() {
|
||||
for i in 0..=255 {
|
||||
assert!(!bit(i, [0u8; 32]))
|
||||
}
|
||||
|
||||
for i in 0..=255 {
|
||||
assert!(bit(i, [255u8; 32]))
|
||||
}
|
||||
|
||||
for i in 0..=255 {
|
||||
assert_eq!(bit(i, [85u8; 32]), i % 2 == 0)
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_empty_tree_root() {
|
||||
assert_eq!(empty_tree_root(0), ABSENT);
|
||||
|
||||
assert_eq!(empty_tree_root(1), merkle::node(ABSENT, ABSENT));
|
||||
assert_eq!(
|
||||
empty_tree_root(2),
|
||||
merkle::node(merkle::node(ABSENT, ABSENT), merkle::node(ABSENT, ABSENT)),
|
||||
);
|
||||
assert_eq!(
|
||||
empty_tree_root(3),
|
||||
merkle::node(
|
||||
merkle::node(merkle::node(ABSENT, ABSENT), merkle::node(ABSENT, ABSENT)),
|
||||
merkle::node(merkle::node(ABSENT, ABSENT), merkle::node(ABSENT, ABSENT)),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,11 @@
|
|||
use crate::cl::{merkle, mmr::MMR, Nullifier};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
const MAX_NULL: usize = 256;
|
||||
use crate::cl::{
|
||||
merkle,
|
||||
mmr::{MMRProof, MMR},
|
||||
sparse_merkle, NoteCommitment, Nullifier,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Ledger {
|
||||
|
@ -12,23 +16,65 @@ pub struct Ledger {
|
|||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct LedgerWitness {
|
||||
pub commitments: MMR,
|
||||
pub nullifiers: Vec<Nullifier>,
|
||||
pub nf_root: [u8; 32],
|
||||
}
|
||||
|
||||
impl LedgerWitness {
|
||||
pub fn commit(&self) -> Ledger {
|
||||
Ledger {
|
||||
cm_root: self.commitments.commit(),
|
||||
nf_root: self.nf_root,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn valid_cm_root(&self, root: [u8; 32]) -> bool {
|
||||
self.commitments.roots.iter().any(|r| r.root == root)
|
||||
}
|
||||
|
||||
pub fn add_commitment(&mut self, cm: &NoteCommitment) {
|
||||
self.commitments.push(&cm.0);
|
||||
}
|
||||
|
||||
pub fn assert_nf_update(&mut self, nf: &Nullifier, path: &[merkle::PathNode]) {
|
||||
// verify that the path corresponds to the nullifier
|
||||
assert_eq!(sparse_merkle::path_key(path), nf.0);
|
||||
|
||||
// verify that the nullifier was not already present
|
||||
assert_eq!(merkle::path_root(sparse_merkle::ABSENT, path), self.nf_root);
|
||||
|
||||
// update the nullifer root with the nullifier inserted into the tree
|
||||
self.nf_root = merkle::path_root(sparse_merkle::PRESENT, path);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct LedgerState {
|
||||
pub commitments: MMR,
|
||||
pub nullifiers: BTreeSet<[u8; 32]>,
|
||||
}
|
||||
|
||||
impl LedgerState {
|
||||
pub fn to_witness(&self) -> LedgerWitness {
|
||||
LedgerWitness {
|
||||
commitments: self.commitments.clone(),
|
||||
nf_root: self.nf_root(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nf_root(&self) -> [u8; 32] {
|
||||
let bytes = self
|
||||
.nullifiers
|
||||
.iter()
|
||||
.map(|i| i.as_bytes().to_vec())
|
||||
.collect::<Vec<_>>();
|
||||
merkle::root(merkle::padded_leaves::<MAX_NULL>(&bytes))
|
||||
sparse_merkle::sparse_root(&self.nullifiers)
|
||||
}
|
||||
|
||||
pub fn add_commitment(&mut self, cm: &NoteCommitment) -> MMRProof {
|
||||
self.commitments.push(&cm.0)
|
||||
}
|
||||
|
||||
pub fn add_nullifier(&mut self, nf: Nullifier) -> Vec<merkle::PathNode> {
|
||||
let path = sparse_merkle::sparse_path(nf.0, &self.nullifiers);
|
||||
|
||||
assert!(!self.nullifiers.contains(&nf.0));
|
||||
self.nullifiers.insert(nf.0);
|
||||
|
||||
path
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ edition = "2021"
|
|||
cl = { path = "../cl" }
|
||||
ledger_proof_statements = { path = "../ledger_proof_statements" }
|
||||
nomos_cl_risc0_proofs = { path = "../risc0_proofs" }
|
||||
nomos_cl_bundle_risc0_proof = { path = "../bundle_risc0_proof" }
|
||||
nomos_cl_ptx_risc0_proof = { path = "../ptx_risc0_proof" }
|
||||
ledger_validity_proof = { path = "../ledger_validity_proof" }
|
||||
risc0-zkvm = { version = "1.0", features = ["prove", "metal"] }
|
||||
risc0-groth16 = { version = "1.0" }
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
use crate::error::{Error, Result};
|
||||
use ledger_proof_statements::balance::{BalancePrivate, BalancePublic};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProvedBalance {
|
||||
pub bundle: BalancePublic,
|
||||
pub risc0_receipt: risc0_zkvm::Receipt,
|
||||
}
|
||||
|
||||
impl ProvedBalance {
|
||||
pub fn prove(balance_witness: &BalancePrivate) -> Result<Self> {
|
||||
//show that the sum of ptx balances is 0
|
||||
let env = risc0_zkvm::ExecutorEnv::builder()
|
||||
.write(&balance_witness)
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let prover = risc0_zkvm::default_prover();
|
||||
|
||||
let start_t = std::time::Instant::now();
|
||||
|
||||
let opts = risc0_zkvm::ProverOpts::succinct();
|
||||
let prove_info = prover
|
||||
.prove_with_opts(env, nomos_cl_risc0_proofs::BALANCE_ELF, &opts)
|
||||
.map_err(|_| Error::Risc0ProofFailed)?;
|
||||
|
||||
println!(
|
||||
"STARK 'bundle' prover time: {:.2?}, total_cycles: {}",
|
||||
start_t.elapsed(),
|
||||
prove_info.stats.total_cycles
|
||||
);
|
||||
|
||||
let receipt = prove_info.receipt;
|
||||
|
||||
Ok(Self {
|
||||
bundle: receipt.journal.decode()?,
|
||||
risc0_receipt: receipt,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn public(&self) -> Result<ledger_proof_statements::balance::BalancePublic> {
|
||||
Ok(self.risc0_receipt.journal.decode()?)
|
||||
}
|
||||
|
||||
pub fn verify(&self) -> bool {
|
||||
// let Ok(_bundle_public) = self.public() else {
|
||||
// return false;
|
||||
// };
|
||||
|
||||
// Vec::from_iter(self.bundle.partials.iter().map(|ptx| ptx.balance)) == bundle_public.balances
|
||||
// &&
|
||||
self.risc0_receipt
|
||||
.verify(nomos_cl_risc0_proofs::BALANCE_ID)
|
||||
.is_ok()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
use ledger_proof_statements::bundle::{BundlePrivate, BundlePublic};
|
||||
|
||||
use crate::partial_tx::ProvedPartialTx;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProvedBundle {
|
||||
pub risc0_receipt: risc0_zkvm::Receipt,
|
||||
}
|
||||
|
||||
impl ProvedBundle {
|
||||
pub fn prove(bundle: &BundlePrivate, partials: Vec<ProvedPartialTx>) -> Self {
|
||||
//show that all ptx's are individually valid, and balance to 0
|
||||
let mut env = risc0_zkvm::ExecutorEnv::builder();
|
||||
|
||||
for proved_ptx in partials {
|
||||
env.add_assumption(proved_ptx.risc0_receipt);
|
||||
}
|
||||
|
||||
let env = env.write(&bundle).unwrap().build().unwrap();
|
||||
|
||||
let prover = risc0_zkvm::default_prover();
|
||||
|
||||
let start_t = std::time::Instant::now();
|
||||
|
||||
let opts = risc0_zkvm::ProverOpts::succinct();
|
||||
let prove_info = prover
|
||||
.prove_with_opts(env, nomos_cl_bundle_risc0_proof::BUNDLE_ELF, &opts)
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
"STARK 'bundle' prover time: {:.2?}, total_cycles: {}",
|
||||
start_t.elapsed(),
|
||||
prove_info.stats.total_cycles
|
||||
);
|
||||
|
||||
let receipt = prove_info.receipt;
|
||||
|
||||
Self {
|
||||
risc0_receipt: receipt,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn public(&self) -> BundlePublic {
|
||||
self.risc0_receipt.journal.decode().unwrap()
|
||||
}
|
||||
|
||||
pub fn verify(&self) -> bool {
|
||||
self.risc0_receipt
|
||||
.verify(nomos_cl_bundle_risc0_proof::BUNDLE_ID)
|
||||
.is_ok()
|
||||
}
|
||||
}
|
|
@ -1,64 +1,60 @@
|
|||
use ledger_proof_statements::{
|
||||
ledger::{LedgerProofPrivate, LedgerProofPublic},
|
||||
ptx::PtxPublic,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::{
|
||||
balance::ProvedBalance,
|
||||
constraint::ConstraintProof,
|
||||
error::{Error, Result},
|
||||
partial_tx::ProvedPartialTx,
|
||||
};
|
||||
use cl::zone_layer::{ledger::LedgerWitness, notes::ZoneId};
|
||||
use ledger_proof_statements::ledger::{LedgerBundleWitness, LedgerProofPrivate, LedgerProofPublic};
|
||||
|
||||
use crate::bundle::ProvedBundle;
|
||||
use cl::zone_layer::{ledger::LedgerState, notes::ZoneId};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProvedLedgerTransition {
|
||||
pub public: LedgerProofPublic,
|
||||
pub risc0_receipt: risc0_zkvm::Receipt,
|
||||
}
|
||||
|
||||
// TODO: find a better name
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProvedBundle {
|
||||
pub balance: ProvedBalance,
|
||||
pub ptxs: Vec<ProvedPartialTx>,
|
||||
}
|
||||
|
||||
impl ProvedBundle {
|
||||
fn to_public(&self) -> Vec<PtxPublic> {
|
||||
self.ptxs.iter().map(|p| p.public.clone()).collect()
|
||||
}
|
||||
|
||||
fn proofs(&self) -> Vec<risc0_zkvm::Receipt> {
|
||||
let mut proofs = vec![self.balance.risc0_receipt.clone()];
|
||||
proofs.extend(self.ptxs.iter().map(|p| p.risc0_receipt.clone()));
|
||||
proofs
|
||||
}
|
||||
}
|
||||
|
||||
impl ProvedLedgerTransition {
|
||||
pub fn prove(
|
||||
ledger: LedgerWitness,
|
||||
zone_id: ZoneId,
|
||||
bundles: Vec<ProvedBundle>,
|
||||
constraints: Vec<ConstraintProof>,
|
||||
) -> Result<Self> {
|
||||
let witness = LedgerProofPrivate {
|
||||
bundles: bundles.iter().map(|p| p.to_public()).collect(),
|
||||
ledger,
|
||||
pub fn prove(mut ledger: LedgerState, zone_id: ZoneId, bundles: Vec<ProvedBundle>) -> Self {
|
||||
let mut witness = LedgerProofPrivate {
|
||||
bundles: Vec::new(),
|
||||
ledger: ledger.to_witness(),
|
||||
id: zone_id,
|
||||
};
|
||||
|
||||
let mut env = risc0_zkvm::ExecutorEnv::builder();
|
||||
|
||||
for bundle in bundles {
|
||||
for proof in bundle.proofs() {
|
||||
env.add_assumption(proof);
|
||||
// prepare the sparse merkle tree nullifier proofs
|
||||
for proved_bundle in &bundles {
|
||||
env.add_assumption(proved_bundle.risc0_receipt.clone());
|
||||
|
||||
let bundle = proved_bundle.public();
|
||||
|
||||
let zone_ledger_update = bundle
|
||||
.zone_ledger_updates
|
||||
.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.cm_roots.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, vec![])
|
||||
}));
|
||||
|
||||
let mut nf_proofs = Vec::new();
|
||||
for nf in &zone_ledger_update.nullifiers {
|
||||
let nf_proof = ledger.add_nullifier(*nf);
|
||||
nf_proofs.push(nf_proof);
|
||||
}
|
||||
|
||||
let ledger_bundle = LedgerBundleWitness {
|
||||
bundle,
|
||||
cm_root_proofs,
|
||||
nf_proofs,
|
||||
};
|
||||
|
||||
witness.bundles.push(ledger_bundle)
|
||||
}
|
||||
for covenant in constraints {
|
||||
env.add_assumption(covenant.risc0_receipt);
|
||||
}
|
||||
|
||||
let env = env.write(&witness).unwrap().build().unwrap();
|
||||
|
||||
// Obtain the default prover.
|
||||
|
@ -71,10 +67,7 @@ impl ProvedLedgerTransition {
|
|||
let opts = risc0_zkvm::ProverOpts::succinct();
|
||||
let prove_info = prover
|
||||
.prove_with_opts(env, ledger_validity_proof::LEDGER_ELF, &opts)
|
||||
.map_err(|e| {
|
||||
eprintln!("{e}");
|
||||
Error::Risc0ProofFailed
|
||||
})?;
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
"STARK 'ledger' prover time: {:.2?}, total_cycles: {}",
|
||||
|
@ -82,14 +75,16 @@ impl ProvedLedgerTransition {
|
|||
prove_info.stats.total_cycles
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
public: prove_info
|
||||
.receipt
|
||||
.journal
|
||||
.decode::<LedgerProofPublic>()
|
||||
.unwrap(),
|
||||
Self {
|
||||
risc0_receipt: prove_info.receipt,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn public(&self) -> LedgerProofPublic {
|
||||
self.risc0_receipt
|
||||
.journal
|
||||
.decode::<LedgerProofPublic>()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn verify(&self) -> bool {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
pub mod balance;
|
||||
pub mod bundle;
|
||||
pub mod constraint;
|
||||
pub mod error;
|
||||
pub mod ledger;
|
||||
|
|
|
@ -1,31 +1,36 @@
|
|||
use ledger_proof_statements::ptx::{PtxPrivate, PtxPublic};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use cl::cl::{merkle, PartialTxWitness};
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
ConstraintProof,
|
||||
};
|
||||
use cl::cl::{
|
||||
mmr::{MMRProof, MMR},
|
||||
PartialTxWitness,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProvedPartialTx {
|
||||
pub public: PtxPublic,
|
||||
pub risc0_receipt: risc0_zkvm::Receipt,
|
||||
}
|
||||
|
||||
impl ProvedPartialTx {
|
||||
pub fn prove(
|
||||
ptx_witness: PartialTxWitness,
|
||||
input_cm_paths: Vec<Vec<merkle::PathNode>>,
|
||||
cm_roots: Vec<[u8; 32]>,
|
||||
input_cm_proofs: Vec<(MMR, MMRProof)>,
|
||||
covenant_proofs: Vec<ConstraintProof>,
|
||||
) -> Result<ProvedPartialTx> {
|
||||
let ptx_private = PtxPrivate {
|
||||
ptx: ptx_witness,
|
||||
input_cm_paths,
|
||||
cm_roots: cm_roots.clone(),
|
||||
input_cm_proofs,
|
||||
};
|
||||
|
||||
let env = risc0_zkvm::ExecutorEnv::builder()
|
||||
.write(&ptx_private)
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
let mut env = risc0_zkvm::ExecutorEnv::builder();
|
||||
|
||||
for covenant_proof in covenant_proofs {
|
||||
env.add_assumption(covenant_proof.risc0_receipt);
|
||||
}
|
||||
let env = env.write(&ptx_private).unwrap().build().unwrap();
|
||||
|
||||
// Obtain the default prover.
|
||||
let prover = risc0_zkvm::default_prover();
|
||||
|
@ -36,7 +41,7 @@ impl ProvedPartialTx {
|
|||
// 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, nomos_cl_risc0_proofs::PTX_ELF, &opts)
|
||||
.prove_with_opts(env, nomos_cl_ptx_risc0_proof::PTX_ELF, &opts)
|
||||
.map_err(|_| Error::Risc0ProofFailed)?;
|
||||
|
||||
println!(
|
||||
|
@ -46,14 +51,17 @@ impl ProvedPartialTx {
|
|||
);
|
||||
|
||||
Ok(Self {
|
||||
public: prove_info.receipt.journal.decode()?,
|
||||
risc0_receipt: prove_info.receipt,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn public(&self) -> PtxPublic {
|
||||
self.risc0_receipt.journal.decode().unwrap()
|
||||
}
|
||||
|
||||
pub fn verify(&self) -> bool {
|
||||
self.risc0_receipt
|
||||
.verify(nomos_cl_risc0_proofs::PTX_ID)
|
||||
.verify(nomos_cl_ptx_risc0_proof::PTX_ID)
|
||||
.is_ok()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,16 +18,15 @@ impl ProvedUpdateBundle {
|
|||
return false;
|
||||
}
|
||||
|
||||
for bundle in &proof.public.cross_bundles {
|
||||
for bundle in &proof.public().cross_bundles {
|
||||
expected_zones.insert(bundle.id, HashSet::from_iter(bundle.zones.clone()));
|
||||
actual_zones
|
||||
.entry(bundle.id)
|
||||
.or_insert_with(|| HashSet::new())
|
||||
.insert(proof.public.id);
|
||||
.or_insert_with(HashSet::new)
|
||||
.insert(proof.public().id);
|
||||
}
|
||||
}
|
||||
|
||||
println!("{:?} | {:?}", expected_zones, actual_zones);
|
||||
for (bundle, expected) in expected_zones.iter() {
|
||||
if let Some(actual) = actual_zones.get(bundle) {
|
||||
if actual != expected {
|
||||
|
@ -49,8 +48,8 @@ impl ProvedUpdateBundle {
|
|||
return false;
|
||||
}
|
||||
|
||||
if ledger_proof.public.old_ledger != update.old.ledger
|
||||
|| ledger_proof.public.ledger != update.new.ledger
|
||||
if ledger_proof.public().old_ledger != update.old.ledger
|
||||
|| ledger_proof.public().ledger != update.new.ledger
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,29 +1,28 @@
|
|||
use cl::{
|
||||
cl::{
|
||||
balance::Unit, merkle, mmr::MMR, note::derive_unit, BalanceWitness, InputWitness,
|
||||
NoteWitness, NullifierCommitment, NullifierSecret, OutputWitness, PartialTxWitness,
|
||||
balance::Unit,
|
||||
mmr::{MMRProof, MMR},
|
||||
note::derive_unit,
|
||||
BalanceWitness, InputWitness, NoteWitness, NullifierCommitment, NullifierSecret,
|
||||
OutputWitness, PartialTxWitness,
|
||||
},
|
||||
zone_layer::{
|
||||
ledger::LedgerWitness,
|
||||
ledger::LedgerState,
|
||||
notes::{ZoneId, ZoneNote},
|
||||
tx::{UpdateBundle, ZoneUpdate},
|
||||
},
|
||||
};
|
||||
use ledger::{
|
||||
balance::ProvedBalance,
|
||||
constraint::ConstraintProof,
|
||||
ledger::{ProvedBundle, ProvedLedgerTransition},
|
||||
partial_tx::ProvedPartialTx,
|
||||
stf::StfProof,
|
||||
zone_update::ProvedUpdateBundle,
|
||||
bundle::ProvedBundle, constraint::ConstraintProof, ledger::ProvedLedgerTransition,
|
||||
partial_tx::ProvedPartialTx, stf::StfProof, zone_update::ProvedUpdateBundle,
|
||||
};
|
||||
use ledger_proof_statements::{balance::BalancePrivate, stf::StfPublic};
|
||||
use ledger_proof_statements::{bundle::BundlePrivate, stf::StfPublic};
|
||||
use rand_core::CryptoRngCore;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
fn nmo() -> &'static Unit {
|
||||
fn nmo() -> Unit {
|
||||
static NMO: OnceLock<Unit> = OnceLock::new();
|
||||
NMO.get_or_init(|| derive_unit("NMO"))
|
||||
*NMO.get_or_init(|| derive_unit("NMO"))
|
||||
}
|
||||
|
||||
struct User(NullifierSecret);
|
||||
|
@ -48,24 +47,22 @@ fn receive_utxo(note: NoteWitness, nf_pk: NullifierCommitment, zone_id: ZoneId)
|
|||
|
||||
fn cross_transfer_transition(
|
||||
input: InputWitness,
|
||||
input_path: Vec<merkle::PathNode>,
|
||||
input_proof: (MMR, MMRProof),
|
||||
to: User,
|
||||
amount: u64,
|
||||
zone_a: ZoneId,
|
||||
zone_b: ZoneId,
|
||||
mut ledger_a: LedgerWitness,
|
||||
mut ledger_b: LedgerWitness,
|
||||
mut ledger_a: LedgerState,
|
||||
mut ledger_b: LedgerState,
|
||||
) -> (ProvedLedgerTransition, ProvedLedgerTransition) {
|
||||
let mut rng = rand::thread_rng();
|
||||
assert!(amount <= input.note.value);
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let change = input.note.value - amount;
|
||||
let transfer = OutputWitness::new(
|
||||
NoteWitness::basic(amount, *nmo(), &mut rng),
|
||||
to.pk(),
|
||||
zone_b,
|
||||
);
|
||||
let transfer = OutputWitness::new(NoteWitness::basic(amount, nmo(), &mut rng), to.pk(), zone_b);
|
||||
let change = OutputWitness::new(
|
||||
NoteWitness::basic(change, *nmo(), &mut rng),
|
||||
NoteWitness::basic(change, nmo(), &mut rng),
|
||||
input.nf_sk.commit(),
|
||||
zone_a,
|
||||
);
|
||||
|
@ -76,45 +73,46 @@ fn cross_transfer_transition(
|
|||
outputs: vec![transfer, change],
|
||||
balance_blinding: BalanceWitness::random_blinding(&mut rng),
|
||||
};
|
||||
let proved_ptx = ProvedPartialTx::prove(
|
||||
ptx_witness.clone(),
|
||||
vec![input_path],
|
||||
vec![ledger_a.commitments.roots[0].root],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let balance = ProvedBalance::prove(&BalancePrivate {
|
||||
balances: vec![ptx_witness.balance()],
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let zone_tx = ProvedBundle {
|
||||
ptxs: vec![proved_ptx.clone()],
|
||||
balance,
|
||||
};
|
||||
|
||||
// Prove the constraints for alices input (she uses the no-op constraint)
|
||||
let constraint_proof =
|
||||
ConstraintProof::prove_nop(input.nullifier(), proved_ptx.public.ptx.root());
|
||||
ConstraintProof::prove_nop(input.nullifier(), ptx_witness.commit().root());
|
||||
|
||||
let ledger_a_transition = ProvedLedgerTransition::prove(
|
||||
ledger_a.clone(),
|
||||
zone_a,
|
||||
vec![zone_tx.clone()],
|
||||
vec![constraint_proof],
|
||||
let proved_ptx = ProvedPartialTx::prove(
|
||||
ptx_witness.clone(),
|
||||
vec![input_proof],
|
||||
vec![constraint_proof.clone()],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let ledger_b_transition =
|
||||
ProvedLedgerTransition::prove(ledger_b.clone(), zone_b, vec![zone_tx], vec![]).unwrap();
|
||||
let bundle = ProvedBundle::prove(
|
||||
&BundlePrivate {
|
||||
bundle: vec![proved_ptx.public()],
|
||||
balances: vec![ptx_witness.balance()],
|
||||
},
|
||||
vec![proved_ptx],
|
||||
);
|
||||
|
||||
ledger_a.commitments.push(&change.commit_note().0);
|
||||
ledger_a.nullifiers.push(input.nullifier());
|
||||
println!("proving ledger A transition");
|
||||
let ledger_a_transition =
|
||||
ProvedLedgerTransition::prove(ledger_a.clone(), zone_a, vec![bundle.clone()]);
|
||||
|
||||
ledger_b.commitments.push(&transfer.commit_note().0);
|
||||
println!("proving ledger B transition");
|
||||
let ledger_b_transition = ProvedLedgerTransition::prove(ledger_b.clone(), zone_b, vec![bundle]);
|
||||
|
||||
assert_eq!(ledger_a_transition.public.ledger, ledger_a.commit());
|
||||
assert_eq!(ledger_b_transition.public.ledger, ledger_b.commit());
|
||||
ledger_a.add_commitment(&change.commit_note());
|
||||
ledger_a.add_nullifier(input.nullifier());
|
||||
|
||||
ledger_b.add_commitment(&transfer.commit_note());
|
||||
|
||||
assert_eq!(
|
||||
ledger_a_transition.public().ledger,
|
||||
ledger_a.to_witness().commit()
|
||||
);
|
||||
assert_eq!(
|
||||
ledger_b_transition.public().ledger,
|
||||
ledger_b.to_witness().commit()
|
||||
);
|
||||
|
||||
(ledger_a_transition, ledger_b_transition)
|
||||
}
|
||||
|
@ -133,42 +131,35 @@ fn zone_update_cross() {
|
|||
|
||||
// Alice has an unspent note worth 10 NMO
|
||||
let utxo = receive_utxo(
|
||||
NoteWitness::stateless(10, *nmo(), ConstraintProof::nop_constraint(), &mut rng),
|
||||
NoteWitness::stateless(10, nmo(), ConstraintProof::nop_constraint(), &mut rng),
|
||||
alice.pk(),
|
||||
zone_a_id,
|
||||
);
|
||||
|
||||
let alice_input = InputWitness::from_output(utxo, alice.sk());
|
||||
|
||||
let mut mmr = MMR::new();
|
||||
let input_cm_path = mmr.push(&utxo.commit_note().0).path;
|
||||
let mut ledger_a = LedgerState::default();
|
||||
let alice_cm_path = ledger_a.add_commitment(&utxo.commit_note());
|
||||
let alice_cm_proof = (ledger_a.commitments.clone(), alice_cm_path);
|
||||
|
||||
let ledger_a = LedgerWitness {
|
||||
commitments: mmr,
|
||||
nullifiers: vec![],
|
||||
};
|
||||
|
||||
let ledger_b = LedgerWitness {
|
||||
commitments: MMR::new(),
|
||||
nullifiers: vec![],
|
||||
};
|
||||
let ledger_b = LedgerState::default();
|
||||
|
||||
let zone_a_old = ZoneNote {
|
||||
id: zone_a_id,
|
||||
state: [0; 32],
|
||||
ledger: ledger_a.commit(),
|
||||
ledger: ledger_a.to_witness().commit(),
|
||||
stf: [0; 32],
|
||||
};
|
||||
let zone_b_old = ZoneNote {
|
||||
id: zone_b_id,
|
||||
state: [0; 32],
|
||||
ledger: ledger_b.commit(),
|
||||
ledger: ledger_b.to_witness().commit(),
|
||||
stf: [0; 32],
|
||||
};
|
||||
|
||||
let (ledger_a_transition, ledger_b_transition) = cross_transfer_transition(
|
||||
alice_input,
|
||||
input_cm_path,
|
||||
alice_cm_proof,
|
||||
bob,
|
||||
8,
|
||||
zone_a_id,
|
||||
|
@ -178,12 +169,12 @@ fn zone_update_cross() {
|
|||
);
|
||||
|
||||
let zone_a_new = ZoneNote {
|
||||
ledger: ledger_a_transition.public.ledger,
|
||||
ledger: ledger_a_transition.public().ledger,
|
||||
..zone_a_old
|
||||
};
|
||||
|
||||
let zone_b_new = ZoneNote {
|
||||
ledger: ledger_b_transition.public.ledger,
|
||||
ledger: ledger_b_transition.public().ledger,
|
||||
..zone_b_old
|
||||
};
|
||||
|
||||
|
|
|
@ -6,3 +6,4 @@ edition = "2021"
|
|||
[dependencies]
|
||||
cl = { path = "../cl" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
sha2 = "0.10"
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
use cl::cl::{Balance, BalanceWitness};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct BalancePublic {
|
||||
pub balances: Vec<Balance>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct BalancePrivate {
|
||||
pub balances: Vec<BalanceWitness>,
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use cl::{
|
||||
cl::{BalanceWitness, NoteCommitment, Nullifier},
|
||||
zone_layer::notes::ZoneId,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::ptx::PtxPublic;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct BundleId(pub [u8; 32]);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct BundlePublic {
|
||||
pub bundle_id: BundleId,
|
||||
pub zone_ledger_updates: BTreeMap<ZoneId, LedgerUpdate>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
pub struct LedgerUpdate {
|
||||
// inputs in this bundle used the following roots in their cm membership proof.
|
||||
pub cm_roots: BTreeSet<[u8; 32]>,
|
||||
// these are the nullifiers of inputs used in this bundle.
|
||||
pub nullifiers: Vec<Nullifier>,
|
||||
// these are commitments to created notes in this bundle
|
||||
pub commitments: Vec<NoteCommitment>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct BundlePrivate {
|
||||
pub bundle: Vec<PtxPublic>,
|
||||
pub balances: Vec<BalanceWitness>,
|
||||
}
|
||||
|
||||
impl BundlePrivate {
|
||||
pub fn id(&self) -> BundleId {
|
||||
// TODO: change to merkle root
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(b"NOMOS_CL_BUNDLE_ID");
|
||||
for ptx in &self.bundle {
|
||||
hasher.update(ptx.ptx.root().0);
|
||||
}
|
||||
|
||||
BundleId(hasher.finalize().into())
|
||||
}
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
use crate::ptx::PtxPublic;
|
||||
use cl::cl::{bundle::BundleId, Output};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::bundle::BundleId;
|
||||
use crate::bundle::BundlePublic;
|
||||
use cl::cl::{merkle, NoteCommitment};
|
||||
use cl::zone_layer::{
|
||||
ledger::{Ledger, LedgerWitness},
|
||||
notes::ZoneId,
|
||||
|
@ -12,14 +15,21 @@ pub struct LedgerProofPublic {
|
|||
pub ledger: Ledger,
|
||||
pub id: ZoneId,
|
||||
pub cross_bundles: Vec<CrossZoneBundle>,
|
||||
pub outputs: Vec<Output>,
|
||||
pub outputs: Vec<NoteCommitment>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct LedgerProofPrivate {
|
||||
pub ledger: LedgerWitness,
|
||||
pub id: ZoneId,
|
||||
pub bundles: Vec<Vec<PtxPublic>>,
|
||||
pub bundles: Vec<LedgerBundleWitness>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct LedgerBundleWitness {
|
||||
pub bundle: BundlePublic,
|
||||
pub cm_root_proofs: BTreeMap<[u8; 32], merkle::Path>,
|
||||
pub nf_proofs: Vec<merkle::Path>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
pub mod balance;
|
||||
pub mod bundle;
|
||||
pub mod constraint;
|
||||
pub mod ledger;
|
||||
pub mod ptx;
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
use cl::cl::{merkle, PartialTx, PartialTxWitness};
|
||||
use cl::cl::{
|
||||
mmr::{MMRProof, MMR},
|
||||
PartialTx, PartialTxWitness,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PtxPublic {
|
||||
pub ptx: PartialTx,
|
||||
pub cm_roots: Vec<[u8; 32]>,
|
||||
pub cm_mmrs: Vec<MMR>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PtxPrivate {
|
||||
pub ptx: PartialTxWitness,
|
||||
pub input_cm_paths: Vec<Vec<merkle::PathNode>>,
|
||||
pub cm_roots: Vec<[u8; 32]>,
|
||||
pub input_cm_proofs: Vec<(MMR, MMRProof)>,
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ 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" }
|
||||
nomos_cl_risc0_proofs = { path = "../../risc0_proofs" }
|
||||
nomos_cl_bundle_risc0_proof = { path = "../../bundle_risc0_proof" }
|
||||
|
||||
[patch.crates-io]
|
||||
# add RISC Zero accelerator support for all downstream usages of the following crates.
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
use cl::{
|
||||
cl::{Bundle, Output},
|
||||
zone_layer::{ledger::LedgerWitness, notes::ZoneId},
|
||||
};
|
||||
use cl::cl::merkle;
|
||||
use ledger_proof_statements::{
|
||||
balance::BalancePublic,
|
||||
constraint::ConstraintPublic,
|
||||
ledger::{CrossZoneBundle, LedgerProofPrivate, LedgerProofPublic},
|
||||
ptx::PtxPublic,
|
||||
ledger::{CrossZoneBundle, LedgerProofPrivate, LedgerProofPublic, LedgerBundleWitness},
|
||||
};
|
||||
use risc0_zkvm::{guest::env, serde};
|
||||
|
||||
|
@ -17,93 +11,42 @@ fn main() {
|
|||
bundles,
|
||||
} = env::read();
|
||||
|
||||
let old_ledger = ledger.commit();
|
||||
let old_ledger = ledger.clone();
|
||||
let mut cross_bundles = vec![];
|
||||
let mut outputs = vec![];
|
||||
|
||||
let roots = ledger
|
||||
.commitments
|
||||
.roots
|
||||
.iter()
|
||||
.map(|r| r.root)
|
||||
.collect::<Vec<_>>();
|
||||
for LedgerBundleWitness { bundle, cm_root_proofs, nf_proofs } in bundles {
|
||||
env::verify(nomos_cl_bundle_risc0_proof::BUNDLE_ID, &serde::to_vec(&bundle).unwrap()).unwrap();
|
||||
|
||||
for bundle in bundles {
|
||||
let balance_public = BalancePublic {
|
||||
balances: bundle.iter().map(|ptx| ptx.ptx.balance).collect::<Vec<_>>(),
|
||||
};
|
||||
// verify bundle is balanced
|
||||
env::verify(
|
||||
nomos_cl_risc0_proofs::BALANCE_ID,
|
||||
&serde::to_vec(&balance_public).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
if let Some(ledger_update) = bundle.zone_ledger_updates.get(&id) {
|
||||
for past_cm_root in &ledger_update.cm_roots {
|
||||
let past_cm_root_proof = cm_root_proofs.get(past_cm_root).expect("missing cm root proof");
|
||||
let expected_current_cm_root = merkle::path_root(*past_cm_root, past_cm_root_proof);
|
||||
assert!(old_ledger.valid_cm_root(expected_current_cm_root))
|
||||
}
|
||||
|
||||
for ptx in &bundle {
|
||||
let (new_ledger, ptx_outputs) = process_ptx(ledger, ptx, id, &roots);
|
||||
ledger = new_ledger;
|
||||
outputs.extend(ptx_outputs);
|
||||
assert_eq!(ledger_update.nullifiers.len(), nf_proofs.len());
|
||||
for (nf, nf_proof) in ledger_update.nullifiers.iter().zip(nf_proofs) {
|
||||
ledger.assert_nf_update(nf, &nf_proof);
|
||||
}
|
||||
|
||||
for cm in &ledger_update.commitments {
|
||||
ledger.add_commitment(cm);
|
||||
outputs.push(*cm)
|
||||
}
|
||||
}
|
||||
|
||||
let bundle = Bundle {
|
||||
partials: bundle.into_iter().map(|ptx| ptx.ptx).collect(),
|
||||
};
|
||||
let zones = bundle.zones();
|
||||
if zones.len() > 1 {
|
||||
cross_bundles.push(CrossZoneBundle {
|
||||
id: bundle.id(),
|
||||
zones: zones.into_iter().collect(),
|
||||
});
|
||||
}
|
||||
cross_bundles.push(CrossZoneBundle {
|
||||
id: bundle.bundle_id,
|
||||
zones: bundle.zone_ledger_updates.into_keys().collect(),
|
||||
});
|
||||
}
|
||||
|
||||
env::commit(&LedgerProofPublic {
|
||||
old_ledger,
|
||||
old_ledger: old_ledger.commit(),
|
||||
ledger: ledger.commit(),
|
||||
id,
|
||||
cross_bundles,
|
||||
outputs,
|
||||
});
|
||||
}
|
||||
|
||||
fn process_ptx(
|
||||
mut ledger: LedgerWitness,
|
||||
ptx: &PtxPublic,
|
||||
zone_id: ZoneId,
|
||||
roots: &[[u8; 32]],
|
||||
) -> (LedgerWitness, Vec<Output>) {
|
||||
// always verify the ptx to ensure outputs were derived with the correct zone id
|
||||
env::verify(nomos_cl_risc0_proofs::PTX_ID, &serde::to_vec(&ptx).unwrap()).unwrap();
|
||||
|
||||
let cm_roots = &ptx.cm_roots;
|
||||
let ptx = &ptx.ptx;
|
||||
|
||||
let mut outputs = vec![];
|
||||
|
||||
for (input, input_cm_root) in ptx.inputs.iter().zip(cm_roots) {
|
||||
if input.zone_id == zone_id {
|
||||
assert!(roots.contains(input_cm_root));
|
||||
assert!(!ledger.nullifiers.contains(&input.nullifier));
|
||||
ledger.nullifiers.push(input.nullifier);
|
||||
|
||||
env::verify(
|
||||
input.constraint.0,
|
||||
&serde::to_vec(&ConstraintPublic {
|
||||
ptx_root: ptx.root(),
|
||||
nf: input.nullifier,
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
for output in &ptx.outputs {
|
||||
if output.zone_id == zone_id {
|
||||
ledger.commitments.push(&output.note_comm.0);
|
||||
outputs.push(*output);
|
||||
}
|
||||
}
|
||||
|
||||
(ledger, outputs)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "nomos_cl_ptx_risc0_proof"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[build-dependencies]
|
||||
risc0-build = { version = "1.0" }
|
||||
|
||||
[package.metadata.risc0]
|
||||
methods = ["ptx"]
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
risc0_build::embed_methods();
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/// Input Proof
|
||||
use ledger_proof_statements::{
|
||||
constraint::ConstraintPublic,
|
||||
ptx::{PtxPrivate, PtxPublic},
|
||||
};
|
||||
use risc0_zkvm::{guest::env, serde};
|
||||
|
||||
fn main() {
|
||||
let PtxPrivate {
|
||||
ptx,
|
||||
input_cm_proofs,
|
||||
} = env::read();
|
||||
|
||||
let ptx_commit = ptx.commit();
|
||||
let ptx_root = ptx_commit.root();
|
||||
|
||||
assert_eq!(ptx.inputs.len(), input_cm_proofs.len());
|
||||
let mut cm_mmrs = Vec::new();
|
||||
for (input, (mmr, mmr_proof)) in ptx.inputs.iter().zip(input_cm_proofs) {
|
||||
let note_cm = input.note_commitment();
|
||||
assert!(mmr.verify_proof(¬e_cm.0, &mmr_proof));
|
||||
cm_mmrs.push(mmr);
|
||||
|
||||
env::verify(
|
||||
input.note.constraint.0,
|
||||
&serde::to_vec(&ConstraintPublic {
|
||||
ptx_root,
|
||||
nf: input.nullifier(),
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for output in ptx.outputs.iter() {
|
||||
assert!(output.note.value > 0);
|
||||
}
|
||||
|
||||
env::commit(&PtxPublic {
|
||||
ptx: ptx_commit,
|
||||
cm_mmrs,
|
||||
});
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
include!(concat!(env!("OUT_DIR"), "/methods.rs"));
|
|
@ -7,5 +7,5 @@ edition = "2021"
|
|||
risc0-build = { version = "1.0" }
|
||||
|
||||
[package.metadata.risc0]
|
||||
methods = ["balance", "constraint_nop", "ptx", "stf_nop"]
|
||||
methods = ["constraint_nop", "stf_nop"]
|
||||
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
use cl::cl::BalanceWitness;
|
||||
/// Bundle Proof
|
||||
///
|
||||
/// The bundle proof demonstrates that the set of partial transactions
|
||||
/// balance to zero. i.e. \sum inputs = \sum outputs.
|
||||
///
|
||||
/// This is done by proving knowledge of some blinding factor `r` s.t.
|
||||
/// \sum outputs - \sum input = 0*G + r*H
|
||||
///
|
||||
/// To avoid doing costly ECC in stark, we compute only the RHS in stark.
|
||||
/// The sums and equality is checked outside of stark during proof verification.
|
||||
use risc0_zkvm::guest::env;
|
||||
|
||||
fn main() {
|
||||
let balance_private: ledger_proof_statements::balance::BalancePrivate = env::read();
|
||||
|
||||
let balance_public = ledger_proof_statements::balance::BalancePublic {
|
||||
balances: Vec::from_iter(balance_private.balances.iter().map(|b| b.commit())),
|
||||
};
|
||||
|
||||
assert!(BalanceWitness::combine(balance_private.balances, [0u8; 16]).is_zero());
|
||||
|
||||
env::commit(&balance_public);
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/// Input Proof
|
||||
use cl::cl::merkle;
|
||||
use ledger_proof_statements::ptx::{PtxPrivate, PtxPublic};
|
||||
use risc0_zkvm::guest::env;
|
||||
|
||||
fn main() {
|
||||
let PtxPrivate {
|
||||
ptx,
|
||||
input_cm_paths,
|
||||
cm_roots,
|
||||
} = env::read();
|
||||
|
||||
assert_eq!(ptx.inputs.len(), input_cm_paths.len());
|
||||
for ((input, cm_path), cm_root) in ptx.inputs.iter().zip(input_cm_paths).zip(&cm_roots) {
|
||||
let note_cm = input.note_commitment();
|
||||
let cm_leaf = merkle::leaf(note_cm.as_bytes());
|
||||
assert_eq!(*cm_root, merkle::path_root(cm_leaf, &cm_path));
|
||||
}
|
||||
|
||||
for output in ptx.outputs.iter() {
|
||||
assert!(output.note.value > 0);
|
||||
}
|
||||
|
||||
env::commit(&PtxPublic {
|
||||
ptx: ptx.commit(),
|
||||
cm_roots,
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue