goas: user intent constraint
This commit is contained in:
parent
da437db677
commit
847e253f10
|
@ -44,21 +44,17 @@ pub struct StateWitness {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StateWitness {
|
impl StateWitness {
|
||||||
/// Merkle tree over:
|
|
||||||
/// root
|
|
||||||
/// / \
|
|
||||||
/// io state
|
|
||||||
/// / \ / \
|
|
||||||
/// nonce txs zoneid balances
|
|
||||||
pub fn commit(&self) -> StateCommitment {
|
pub fn commit(&self) -> StateCommitment {
|
||||||
let root = cl::merkle::root([
|
self.state_roots().commit()
|
||||||
self.nonce,
|
}
|
||||||
self.included_txs_root(),
|
|
||||||
self.zone_metadata.id(),
|
|
||||||
self.balances_root(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
StateCommitment(root)
|
pub fn state_roots(&self) -> StateRoots {
|
||||||
|
StateRoots {
|
||||||
|
nonce: self.nonce,
|
||||||
|
tx_root: self.included_txs_root(),
|
||||||
|
zone_id: self.zone_metadata.id(),
|
||||||
|
balance_root: self.balances_root(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn withdraw(mut self, w: Withdraw) -> Self {
|
pub fn withdraw(mut self, w: Withdraw) -> Self {
|
||||||
|
@ -207,3 +203,35 @@ pub struct IncludedTxWitness {
|
||||||
pub tx: Tx,
|
pub tx: Tx,
|
||||||
pub path: Vec<merkle::PathNode>,
|
pub path: Vec<merkle::PathNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IncludedTxWitness {
|
||||||
|
pub fn tx_root(&self) -> [u8; 32] {
|
||||||
|
let leaf = merkle::leaf(&self.tx.to_bytes());
|
||||||
|
merkle::path_root(leaf, &self.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct StateRoots {
|
||||||
|
pub nonce: [u8; 32],
|
||||||
|
pub tx_root: [u8; 32],
|
||||||
|
pub zone_id: [u8; 32],
|
||||||
|
pub balance_root: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StateRoots {
|
||||||
|
/// Merkle tree over:
|
||||||
|
/// root
|
||||||
|
/// / \
|
||||||
|
/// io state
|
||||||
|
/// / \ / \
|
||||||
|
/// nonce txs zoneid balances
|
||||||
|
pub fn commit(&self) -> StateCommitment {
|
||||||
|
StateCommitment(cl::merkle::root([
|
||||||
|
self.nonce,
|
||||||
|
self.tx_root,
|
||||||
|
self.zone_id,
|
||||||
|
self.balance_root,
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,4 +6,6 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
common = { path = "../common" }
|
common = { path = "../common" }
|
||||||
cl = { path = "../../cl/cl" }
|
cl = { path = "../../cl/cl" }
|
||||||
|
ledger_proof_statements = { path = "../../cl/ledger_proof_statements" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
sha2 = "0.10"
|
||||||
|
|
|
@ -1,2 +1,14 @@
|
||||||
|
pub mod user_note;
|
||||||
pub mod zone_funds;
|
pub mod zone_funds;
|
||||||
pub mod zone_state;
|
pub mod zone_state;
|
||||||
|
|
||||||
|
pub fn assert_is_zone_note(
|
||||||
|
zone_meta: &common::ZoneMetadata,
|
||||||
|
note: &cl::NoteWitness,
|
||||||
|
state_roots: &common::StateRoots,
|
||||||
|
) {
|
||||||
|
assert_eq!(state_roots.commit().0, note.state);
|
||||||
|
assert_eq!(zone_meta.id(), state_roots.zone_id);
|
||||||
|
assert_eq!(zone_meta.zone_vk, note.death_constraint);
|
||||||
|
assert_eq!(zone_meta.unit, note.unit);
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
///
|
///
|
||||||
/// The User Notes death constraint requires the following statements to be satisfied
|
/// The User Notes death constraint requires the following statements to be satisfied
|
||||||
/// in order for the fee to be captured.
|
/// in order for the fee to be captured.
|
||||||
///
|
///
|
||||||
/// 1. w_tx = withdraw(amt=100 NMO, from=Alice) tx was included in Zone A.
|
/// 1. w_tx = withdraw(amt=100 NMO, from=Alice) tx was included in Zone A.
|
||||||
/// 2. d_tx = deposit(amt=100 NMO, to=Alice) tx was included in Zone B.
|
/// 2. d_tx = deposit(amt=100 NMO, to=Alice) tx was included in Zone B.
|
||||||
/// 3. w_tx is included in Zone A iff d_tx is included in Zone B
|
/// 3. w_tx is included in Zone A iff d_tx is included in Zone B
|
||||||
|
@ -22,3 +22,87 @@
|
||||||
/// Details:
|
/// Details:
|
||||||
/// - the withdrawal in zone A must not be a general withdrawal tx, it must be bound to the user note.
|
/// - the withdrawal in zone A must not be a general withdrawal tx, it must be bound to the user note.
|
||||||
/// i.e. the user_note must be present in the ptx for the withdrawal to be valid in Zone A.
|
/// i.e. the user_note must be present in the ptx for the withdrawal to be valid in Zone A.
|
||||||
|
use ledger_proof_statements::death_constraint::DeathConstraintPublic;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct UserIntent {
|
||||||
|
pub zone_a_meta: common::ZoneMetadata,
|
||||||
|
pub zone_b_meta: common::ZoneMetadata,
|
||||||
|
pub withdraw: common::Withdraw,
|
||||||
|
pub deposit: common::Deposit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserIntent {
|
||||||
|
pub fn commit(&self) -> [u8; 32] {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(b"USER_INTENT_STATE");
|
||||||
|
hasher.update(self.zone_a_meta.id());
|
||||||
|
hasher.update(self.zone_b_meta.id());
|
||||||
|
hasher.update(self.withdraw.to_bytes());
|
||||||
|
hasher.update(self.deposit.to_bytes());
|
||||||
|
hasher.finalize().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct UserAtomicTransfer {
|
||||||
|
// user's note
|
||||||
|
pub user_note: cl::PartialTxInputWitness,
|
||||||
|
pub user_intent: UserIntent,
|
||||||
|
|
||||||
|
// the output state notes which should have included both tx's
|
||||||
|
pub zone_a: cl::PartialTxOutputWitness,
|
||||||
|
pub zone_b: cl::PartialTxOutputWitness,
|
||||||
|
|
||||||
|
// proofs of identies of the above notes
|
||||||
|
pub zone_a_roots: common::StateRoots,
|
||||||
|
pub zone_b_roots: common::StateRoots,
|
||||||
|
|
||||||
|
// proof that zone_a has included this withdrawal
|
||||||
|
pub withdraw_tx: common::IncludedTxWitness,
|
||||||
|
// proof that zone_b has included this deposit
|
||||||
|
pub deposit_tx: common::IncludedTxWitness,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserAtomicTransfer {
|
||||||
|
pub fn assert_constraints(&self) -> DeathConstraintPublic {
|
||||||
|
// user committed to these actions in the user note
|
||||||
|
assert_eq!(self.user_intent.commit(), self.user_note.input.note.state);
|
||||||
|
|
||||||
|
// ensure we are interacting with the correct zone notes
|
||||||
|
crate::assert_is_zone_note(
|
||||||
|
&self.user_intent.zone_a_meta,
|
||||||
|
&self.zone_a.output.note,
|
||||||
|
&self.zone_a_roots,
|
||||||
|
);
|
||||||
|
crate::assert_is_zone_note(
|
||||||
|
&self.user_intent.zone_b_meta,
|
||||||
|
&self.zone_b.output.note,
|
||||||
|
&self.zone_b_roots,
|
||||||
|
);
|
||||||
|
|
||||||
|
// ensure txs were included in the respective zones
|
||||||
|
assert_eq!(self.withdraw_tx.tx_root(), self.zone_a_roots.tx_root);
|
||||||
|
assert_eq!(self.deposit_tx.tx_root(), self.zone_b_roots.tx_root);
|
||||||
|
|
||||||
|
// ensure the txs are the same ones the user requested
|
||||||
|
assert_eq!(
|
||||||
|
common::Tx::Withdraw(self.user_intent.withdraw),
|
||||||
|
self.withdraw_tx.tx
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
common::Tx::Deposit(self.user_intent.deposit),
|
||||||
|
self.deposit_tx.tx
|
||||||
|
);
|
||||||
|
|
||||||
|
let input_root = self.user_note.input_root();
|
||||||
|
let output_root = self.zone_a.output_root();
|
||||||
|
assert_eq!(output_root, self.zone_b.output_root());
|
||||||
|
|
||||||
|
let ptx_root = cl::PtxRoot(cl::merkle::node(input_root, output_root));
|
||||||
|
let nf = self.user_note.input.nullifier();
|
||||||
|
DeathConstraintPublic { ptx_root, nf }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,5 +7,4 @@ edition = "2021"
|
||||||
risc0-build = { version = "1.0" }
|
risc0-build = { version = "1.0" }
|
||||||
|
|
||||||
[package.metadata.risc0]
|
[package.metadata.risc0]
|
||||||
methods = ["spend_zone_funds", "zone_state"]
|
methods = ["spend_zone_funds", "zone_state", "user_atomic_transfer"]
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
[package]
|
||||||
|
name = "user_atomic_transfer"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
risc0-zkvm = { version = "1.0", default-features = false, features = ['std'] }
|
||||||
|
blake2 = "0.10"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
bincode = "1"
|
||||||
|
common = { path = "../../common" }
|
||||||
|
cl = { path = "../../../cl/cl" }
|
||||||
|
goas_proof_statements = { path = "../../proof_statements" }
|
||||||
|
ledger_proof_statements = { path = "../../../cl/ledger_proof_statements" }
|
||||||
|
sha2 = "0.10"
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
# Placing these patch statement in the workspace Cargo.toml will add RISC Zero SHA-256 and bigint
|
||||||
|
# multiplication 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" }
|
||||||
|
# k256 = { git = "https://github.com/risc0/RustCrypto-elliptic-curves", tag = "k256/v0.13.3-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" }
|
|
@ -0,0 +1,9 @@
|
||||||
|
use goas_proof_statements::user_note::UserAtomicTransfer;
|
||||||
|
use ledger_proof_statements::death_constraint::DeathConstraintPublic;
|
||||||
|
use risc0_zkvm::guest::env;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let transfer: UserAtomicTransfer = env::read();
|
||||||
|
let public: DeathConstraintPublic = transfer.assert_constraints();
|
||||||
|
env::commit(&public);
|
||||||
|
}
|
Loading…
Reference in New Issue