Merge pull request #23 from logos-co/aat/user-atomic-transfer-constraint
goas: user atomic transfer constraint
This commit is contained in:
commit
1e49131c12
|
@ -44,21 +44,17 @@ pub struct StateWitness {
|
|||
}
|
||||
|
||||
impl StateWitness {
|
||||
/// Merkle tree over:
|
||||
/// root
|
||||
/// / \
|
||||
/// io state
|
||||
/// / \ / \
|
||||
/// nonce txs zoneid balances
|
||||
pub fn commit(&self) -> StateCommitment {
|
||||
let root = cl::merkle::root([
|
||||
self.nonce,
|
||||
self.included_txs_root(),
|
||||
self.zone_metadata.id(),
|
||||
self.balances_root(),
|
||||
]);
|
||||
self.state_roots().commit()
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -207,3 +203,35 @@ pub struct IncludedTxWitness {
|
|||
pub tx: Tx,
|
||||
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]
|
||||
common = { path = "../common" }
|
||||
cl = { path = "../../cl/cl" }
|
||||
ledger_proof_statements = { path = "../../cl/ledger_proof_statements" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
sha2 = "0.10"
|
||||
|
|
|
@ -1,2 +1,14 @@
|
|||
pub mod user_note;
|
||||
pub mod zone_funds;
|
||||
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
|
||||
/// in order for the fee to be captured.
|
||||
///
|
||||
///
|
||||
/// 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.
|
||||
/// 3. w_tx is included in Zone A iff d_tx is included in Zone B
|
||||
|
@ -22,3 +22,87 @@
|
|||
/// Details:
|
||||
/// - 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.
|
||||
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" }
|
||||
|
||||
[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