mirror of
https://github.com/logos-co/nomos-pocs.git
synced 2025-01-12 02:14:35 +00:00
commit
fddba3db2d
@ -8,4 +8,5 @@ serde = { version = "1", features = ["derive"] }
|
|||||||
cl = { path = "../../cl/cl" }
|
cl = { path = "../../cl/cl" }
|
||||||
goas_proof_statements = { path = "../proof_statements" }
|
goas_proof_statements = { path = "../proof_statements" }
|
||||||
proof_statements = { path = "../../cl/proof_statements" }
|
proof_statements = { path = "../../cl/proof_statements" }
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
|
sha2 = "0.10"
|
@ -7,6 +7,7 @@ use cl::{
|
|||||||
};
|
};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
// TODO: sparse merkle tree
|
// TODO: sparse merkle tree
|
||||||
@ -20,27 +21,49 @@ pub struct StateCommitment([u8; 32]);
|
|||||||
|
|
||||||
pub type AccountId = u32;
|
pub type AccountId = u32;
|
||||||
|
|
||||||
// PLACEHOLDER: replace with the death constraint vk of the zone funds
|
|
||||||
pub const ZONE_FUNDS_VK: [u8; 32] = [0; 32];
|
|
||||||
// PLACEHOLDER: this is probably going to be NMO?
|
// PLACEHOLDER: this is probably going to be NMO?
|
||||||
pub static ZONE_CL_FUNDS_UNIT: Lazy<Unit> = Lazy::new(|| crypto::hash_to_curve(b"NMO"));
|
pub static ZONE_CL_FUNDS_UNIT: Lazy<Unit> = Lazy::new(|| crypto::hash_to_curve(b"NMO"));
|
||||||
// PLACEHOLDER
|
|
||||||
pub static ZONE_UNIT: Lazy<Unit> = Lazy::new(|| crypto::hash_to_curve(b"ZONE_UNIT"));
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
// PLACEHOLDER
|
pub struct ZoneMetadata {
|
||||||
pub const ZONE_NF_PK: NullifierCommitment = NullifierCommitment::from_bytes([0; 32]);
|
pub zone_vk: [u8; 32],
|
||||||
|
pub funds_vk: [u8; 32],
|
||||||
|
pub unit: Unit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZoneMetadata {
|
||||||
|
pub fn id(&self) -> [u8; 32] {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(&self.zone_vk);
|
||||||
|
hasher.update(&self.funds_vk);
|
||||||
|
hasher.update(self.unit.compress().as_bytes());
|
||||||
|
hasher.finalize().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct StateWitness {
|
pub struct StateWitness {
|
||||||
pub balances: BTreeMap<u32, u32>,
|
pub balances: BTreeMap<u32, u32>,
|
||||||
pub included_txs: Vec<Input>,
|
pub included_txs: Vec<Input>,
|
||||||
pub output_events: Vec<Event>,
|
pub output_events: Vec<Event>,
|
||||||
|
pub zone_metadata: ZoneMetadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StateWitness {
|
impl StateWitness {
|
||||||
|
/// Merkle tree over:
|
||||||
|
/// root
|
||||||
|
/// / \
|
||||||
|
/// io state
|
||||||
|
/// / \ / \
|
||||||
|
/// events txs zoneid balances
|
||||||
pub fn commit(&self) -> StateCommitment {
|
pub fn commit(&self) -> StateCommitment {
|
||||||
let root = self.balances_root();
|
let root = cl::merkle::root([
|
||||||
let root = cl::merkle::node(self.events_root(), root);
|
self.events_root(),
|
||||||
let root = cl::merkle::node(self.included_txs_root(), root);
|
self.included_txs_root(),
|
||||||
|
self.zone_metadata.id(),
|
||||||
|
self.balances_root(),
|
||||||
|
]);
|
||||||
|
|
||||||
StateCommitment(root)
|
StateCommitment(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,10 @@ pub struct SpendFundsPrivate {
|
|||||||
pub spent_note: PartialTxOutputPrivate,
|
pub spent_note: PartialTxOutputPrivate,
|
||||||
/// The event emitted by the zone that authorizes the spend
|
/// The event emitted by the zone that authorizes the spend
|
||||||
pub spend_event: Spend,
|
pub spend_event: Spend,
|
||||||
/// Path to the zone output state
|
/// Path to the zone output events root
|
||||||
pub spend_event_state_path: Vec<cl::merkle::PathNode>,
|
pub spend_event_state_path: Vec<cl::merkle::PathNode>,
|
||||||
|
/// Merkle root of txs included in the zone
|
||||||
|
pub txs_root: [u8; 32],
|
||||||
|
/// Merkle root of balances in the zone
|
||||||
|
pub balances_root: [u8; 32],
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,10 @@
|
|||||||
///
|
///
|
||||||
/// Our goal: prove the zone authorized spending of funds
|
/// Our goal: prove the zone authorized spending of funds
|
||||||
use cl::merkle;
|
use cl::merkle;
|
||||||
use cl::nullifier::{Nullifier, NullifierNonce, NullifierSecret};
|
use cl::nullifier::{Nullifier, NullifierSecret};
|
||||||
use goas_proof_statements::zone_funds::SpendFundsPrivate;
|
use goas_proof_statements::zone_funds::SpendFundsPrivate;
|
||||||
use proof_statements::death_constraint::DeathConstraintPublic;
|
use proof_statements::death_constraint::DeathConstraintPublic;
|
||||||
use risc0_zkvm::guest::env;
|
use risc0_zkvm::guest::env;
|
||||||
use sha2::{Digest, Sha256};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let SpendFundsPrivate {
|
let SpendFundsPrivate {
|
||||||
@ -16,20 +15,34 @@ fn main() {
|
|||||||
spent_note,
|
spent_note,
|
||||||
spend_event,
|
spend_event,
|
||||||
spend_event_state_path,
|
spend_event_state_path,
|
||||||
|
txs_root,
|
||||||
|
balances_root,
|
||||||
} = env::read();
|
} = env::read();
|
||||||
|
|
||||||
let ptx_root = in_zone_funds.ptx_root();
|
let ptx_root = in_zone_funds.ptx_root();
|
||||||
let nf = Nullifier::new(in_zone_funds.input.nf_sk, in_zone_funds.input.nonce);
|
let nf = Nullifier::new(in_zone_funds.input.nf_sk, in_zone_funds.input.nonce);
|
||||||
// check the zone funds note is the one in the spend event
|
// check the zone funds note is the one in the spend event
|
||||||
assert_eq!(nf, spend_event.nf);
|
assert_eq!(nf, spend_event.nf);
|
||||||
|
|
||||||
assert_eq!(ptx_root, zone_note.ptx_root());
|
assert_eq!(ptx_root, zone_note.ptx_root());
|
||||||
// assert the spent event was an output of the zone stf
|
|
||||||
|
// ** Assert the spent event was an output of the correct zone stf **
|
||||||
|
// The zone state field is a merkle tree over:
|
||||||
|
// root
|
||||||
|
// / \
|
||||||
|
// io state
|
||||||
|
// / \ / \
|
||||||
|
// events txs zoneid balances
|
||||||
|
// We need to check that:
|
||||||
|
// 1) There is a valid path from the spend event to the events root
|
||||||
|
// 2) The zone id matches the one in the current funds note state
|
||||||
|
// 3) The witnesses for spend path, txs and balances allow to calculate the correct root
|
||||||
|
let zone_id = in_zone_funds.input.note.state; // TODO: is there more state?
|
||||||
let spend_event_leaf = merkle::leaf(&spend_event.to_bytes());
|
let spend_event_leaf = merkle::leaf(&spend_event.to_bytes());
|
||||||
// TODO: zones will have some more state
|
let event_root = merkle::path_root(spend_event_leaf, &spend_event_state_path);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
zone_note.output.note.state,
|
merkle::root([event_root, txs_root, zone_id, balances_root]),
|
||||||
merkle::path_root(spend_event_leaf, &spend_event_state_path)
|
zone_note.output.note.state
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(ptx_root, out_zone_funds.ptx_root());
|
assert_eq!(ptx_root, out_zone_funds.ptx_root());
|
||||||
@ -62,7 +75,12 @@ fn main() {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
out_zone_funds.output.nonce,
|
out_zone_funds.output.nonce,
|
||||||
NullifierNonce::from_bytes(Sha256::digest(&out_zone_funds.output.nonce.as_bytes()).into())
|
in_zone_funds.input.evolved_nonce()
|
||||||
|
);
|
||||||
|
// the state is propagated
|
||||||
|
assert_eq!(
|
||||||
|
out_zone_funds.output.note.state,
|
||||||
|
in_zone_funds.input.note.state
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(ptx_root, spent_note.ptx_root());
|
assert_eq!(ptx_root, spent_note.ptx_root());
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use cl::{
|
use cl::{
|
||||||
input::InputWitness,
|
input::InputWitness,
|
||||||
merkle,
|
merkle,
|
||||||
nullifier::{Nullifier, NullifierNonce, NullifierSecret},
|
nullifier::{Nullifier, NullifierSecret},
|
||||||
partial_tx::{MAX_INPUTS, MAX_OUTPUTS},
|
partial_tx::{MAX_INPUTS, MAX_OUTPUTS},
|
||||||
PtxRoot,
|
PtxRoot,
|
||||||
};
|
};
|
||||||
@ -13,7 +13,6 @@ use proof_statements::{
|
|||||||
ptx::{PartialTxInputPrivate, PartialTxOutputPrivate},
|
ptx::{PartialTxInputPrivate, PartialTxOutputPrivate},
|
||||||
};
|
};
|
||||||
use risc0_zkvm::guest::env;
|
use risc0_zkvm::guest::env;
|
||||||
use sha2::{Digest, Sha256};
|
|
||||||
|
|
||||||
fn withdraw(mut state: StateWitness, withdraw: Withdraw) -> StateWitness {
|
fn withdraw(mut state: StateWitness, withdraw: Withdraw) -> StateWitness {
|
||||||
state.included_txs.push(Input::Withdraw(withdraw));
|
state.included_txs.push(Input::Withdraw(withdraw));
|
||||||
@ -54,6 +53,8 @@ fn deposit(
|
|||||||
zone_funds_out,
|
zone_funds_out,
|
||||||
} = deposit;
|
} = deposit;
|
||||||
|
|
||||||
|
let funds_vk = state.zone_metadata.funds_vk;
|
||||||
|
|
||||||
// 1) Check there are no more input/output notes than expected
|
// 1) Check there are no more input/output notes than expected
|
||||||
let inputs = [
|
let inputs = [
|
||||||
deposit.commit().to_bytes().to_vec(),
|
deposit.commit().to_bytes().to_vec(),
|
||||||
@ -74,13 +75,13 @@ fn deposit(
|
|||||||
assert_eq!(ptx_root, pub_inputs.ptx_root);
|
assert_eq!(ptx_root, pub_inputs.ptx_root);
|
||||||
|
|
||||||
// 2) Check the deposit note is not already under control of the zone
|
// 2) Check the deposit note is not already under control of the zone
|
||||||
assert_ne!(deposit.note.death_constraint, ZONE_FUNDS_VK);
|
assert_ne!(deposit.note.death_constraint, funds_vk);
|
||||||
|
|
||||||
// 3) Check the ptx is balanced. This is not a requirement for standard ptxs, but we need it
|
// 3) Check the ptx is balanced. This is not a requirement for standard ptxs, but we need it
|
||||||
// in deposits (at least in a first version) to ensure fund tracking
|
// in deposits (at least in a first version) to ensure fund tracking
|
||||||
assert_eq!(deposit.note.unit, *ZONE_UNIT);
|
assert_eq!(deposit.note.unit, *ZONE_CL_FUNDS_UNIT);
|
||||||
assert_eq!(zone_funds_in.note.unit, *ZONE_UNIT);
|
assert_eq!(zone_funds_in.note.unit, *ZONE_CL_FUNDS_UNIT);
|
||||||
assert_eq!(zone_funds_out.note.unit, *ZONE_UNIT);
|
assert_eq!(zone_funds_out.note.unit, *ZONE_CL_FUNDS_UNIT);
|
||||||
|
|
||||||
let in_sum = deposit.note.value + zone_funds_in.note.value;
|
let in_sum = deposit.note.value + zone_funds_in.note.value;
|
||||||
|
|
||||||
@ -89,15 +90,14 @@ fn deposit(
|
|||||||
assert_eq!(out_sum, in_sum, "deposit ptx is unbalanced");
|
assert_eq!(out_sum, in_sum, "deposit ptx is unbalanced");
|
||||||
|
|
||||||
// 4) Check the zone fund notes are correctly created
|
// 4) Check the zone fund notes are correctly created
|
||||||
assert_eq!(zone_funds_in.note.death_constraint, ZONE_FUNDS_VK);
|
assert_eq!(zone_funds_in.note.death_constraint, funds_vk);
|
||||||
assert_eq!(zone_funds_out.note.death_constraint, ZONE_FUNDS_VK);
|
assert_eq!(zone_funds_out.note.death_constraint, funds_vk);
|
||||||
|
assert_eq!(zone_funds_in.note.state, state.zone_metadata.id());
|
||||||
|
assert_eq!(zone_funds_out.note.state, state.zone_metadata.id());
|
||||||
assert_eq!(zone_funds_in.nf_sk, NullifierSecret::from_bytes([0; 16])); // there is no secret in the zone funds
|
assert_eq!(zone_funds_in.nf_sk, NullifierSecret::from_bytes([0; 16])); // there is no secret in the zone funds
|
||||||
assert_eq!(zone_funds_out.nf_pk, zone_funds_in.nf_sk.commit()); // the sk is the same
|
assert_eq!(zone_funds_out.nf_pk, zone_funds_in.nf_sk.commit()); // the sk is the same
|
||||||
// nonce is correctly evolved
|
// nonce is correctly evolved
|
||||||
assert_eq!(
|
assert_eq!(zone_funds_out.nonce, zone_funds_in.evolved_nonce());
|
||||||
zone_funds_out.nonce,
|
|
||||||
NullifierNonce::from_bytes(Sha256::digest(&zone_funds_in.nonce.as_bytes()).into())
|
|
||||||
);
|
|
||||||
|
|
||||||
// 5) Check zone state notes are correctly created
|
// 5) Check zone state notes are correctly created
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -109,10 +109,7 @@ fn deposit(
|
|||||||
assert_eq!(zone_note_in.note.unit, zone_note_out.note.unit);
|
assert_eq!(zone_note_in.note.unit, zone_note_out.note.unit);
|
||||||
assert_eq!(zone_note_in.note.value, zone_note_out.note.value);
|
assert_eq!(zone_note_in.note.value, zone_note_out.note.value);
|
||||||
// nonce is correctly evolved
|
// nonce is correctly evolved
|
||||||
assert_eq!(
|
assert_eq!(zone_note_out.nonce, zone_note_in.evolved_nonce());
|
||||||
zone_note_out.nonce,
|
|
||||||
NullifierNonce::from_bytes(Sha256::digest(&zone_note_in.nonce.as_bytes()).into())
|
|
||||||
);
|
|
||||||
let nullifier = Nullifier::new(zone_note_in.nf_sk, zone_note_in.nonce);
|
let nullifier = Nullifier::new(zone_note_in.nf_sk, zone_note_in.nonce);
|
||||||
assert_eq!(nullifier, pub_inputs.nf);
|
assert_eq!(nullifier, pub_inputs.nf);
|
||||||
|
|
||||||
@ -136,6 +133,11 @@ fn validate_zone_input(
|
|||||||
let nf = Nullifier::new(input.input.nf_sk, input.input.nonce);
|
let nf = Nullifier::new(input.input.nf_sk, input.input.nonce);
|
||||||
|
|
||||||
assert_eq!(input.input.note.state, <[u8; 32]>::from(state.commit()));
|
assert_eq!(input.input.note.state, <[u8; 32]>::from(state.commit()));
|
||||||
|
// should not be possible to create one but let's put this check here just in case
|
||||||
|
debug_assert_eq!(
|
||||||
|
input.input.note.death_constraint,
|
||||||
|
state.zone_metadata.zone_vk
|
||||||
|
);
|
||||||
|
|
||||||
(ptx_root, nf)
|
(ptx_root, nf)
|
||||||
}
|
}
|
||||||
@ -149,16 +151,13 @@ fn validate_zone_output(
|
|||||||
assert_eq!(ptx, output.ptx_root()); // the ptx root is the same as in the input
|
assert_eq!(ptx, output.ptx_root()); // the ptx root is the same as in the input
|
||||||
let output = output.output;
|
let output = output.output;
|
||||||
assert_eq!(output.note.state, <[u8; 32]>::from(state.commit())); // the state in the output is as calculated by this function
|
assert_eq!(output.note.state, <[u8; 32]>::from(state.commit())); // the state in the output is as calculated by this function
|
||||||
assert_eq!(output.note.death_constraint, input.note.death_constraint); // the death constraint is the same as the on in the input
|
assert_eq!(output.note.death_constraint, state.zone_metadata.zone_vk); // the death constraint is the correct one
|
||||||
assert_eq!(output.nf_pk, NullifierSecret::from_bytes([0; 16]).commit()); // the nullifier secret is public
|
assert_eq!(output.nf_pk, NullifierSecret::from_bytes([0; 16]).commit()); // the nullifier secret is public
|
||||||
assert_eq!(output.balance_blinding, input.balance_blinding); // the balance blinding is the same as in the input
|
assert_eq!(output.balance_blinding, input.balance_blinding); // the balance blinding is the same as in the input
|
||||||
assert_eq!(output.note.unit, input.note.unit); // the balance unit is the same as in the input
|
assert_eq!(output.note.unit, state.zone_metadata.unit); // the balance unit is the same as in the input
|
||||||
|
|
||||||
// the nonce is correctly evolved
|
// the nonce is correctly evolved
|
||||||
assert_eq!(
|
assert_eq!(output.nonce, input.evolved_nonce());
|
||||||
output.nonce,
|
|
||||||
NullifierNonce::from_bytes(Sha256::digest(&input.nonce.as_bytes()).into())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -41,13 +41,17 @@ impl InputWitness {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn evolved_nonce(&self) -> NullifierNonce {
|
||||||
|
self.nonce.evolve(&self.nf_sk)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn evolve_output(&self, balance_blinding: BalanceWitness) -> crate::OutputWitness {
|
pub fn evolve_output(&self, balance_blinding: BalanceWitness) -> crate::OutputWitness {
|
||||||
crate::OutputWitness {
|
crate::OutputWitness {
|
||||||
note: self.note,
|
note: self.note,
|
||||||
balance_blinding,
|
balance_blinding,
|
||||||
nf_pk: self.nf_sk.commit(),
|
nf_pk: self.nf_sk.commit(),
|
||||||
nonce: self.nonce.evolve(&self.nf_sk),
|
nonce: self.evolved_nonce(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nullifier(&self) -> Nullifier {
|
pub fn nullifier(&self) -> Nullifier {
|
||||||
@ -63,7 +67,7 @@ impl InputWitness {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn note_commitment(&self) -> crate::NoteCommitment {
|
pub fn note_commitment(&self) -> crate::NoteCommitment {
|
||||||
self.note.commit(self.nf_sk.commit(), self.nonce)
|
self.note.commit(self.nf_sk.commit(), self.nonce)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user