Deposit (#8)
* Add native zone deposits * check note unit and death constraints * fix deposit logic * fix ptx merkle root derivation * restrict ptx inputs/outputs * re-org directories
This commit is contained in:
parent
e9d43eaee9
commit
7e67014042
|
@ -13,6 +13,9 @@ lazy_static! {
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub struct Balance(pub RistrettoPoint);
|
pub struct Balance(pub RistrettoPoint);
|
||||||
|
|
||||||
|
pub type Value = u64;
|
||||||
|
pub type Unit = RistrettoPoint;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub struct BalanceWitness(pub Scalar);
|
pub struct BalanceWitness(pub Scalar);
|
||||||
|
|
||||||
|
@ -46,7 +49,7 @@ impl BalanceWitness {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn balance(value: u64, unit: RistrettoPoint, blinding: Scalar) -> RistrettoPoint {
|
pub fn balance(value: u64, unit: Unit, blinding: Scalar) -> Unit {
|
||||||
let value_scalar = Scalar::from(value);
|
let value_scalar = Scalar::from(value);
|
||||||
// can vartime leak the number of cycles through the stark proof?
|
// can vartime leak the number of cycles through the stark proof?
|
||||||
RistrettoPoint::vartime_multiscalar_mul(
|
RistrettoPoint::vartime_multiscalar_mul(
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use curve25519_dalek::RistrettoPoint;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
use crate::nullifier::{NullifierCommitment, NullifierNonce};
|
use crate::{
|
||||||
|
balance::Unit,
|
||||||
|
nullifier::{NullifierCommitment, NullifierNonce},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
pub struct DeathCommitment(pub [u8; 32]);
|
pub struct DeathCommitment(pub [u8; 32]);
|
||||||
|
@ -16,7 +18,7 @@ pub fn death_commitment(death_constraint: &[u8]) -> DeathCommitment {
|
||||||
DeathCommitment(death_cm)
|
DeathCommitment(death_cm)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unit_point(unit: &str) -> RistrettoPoint {
|
pub fn unit_point(unit: &str) -> Unit {
|
||||||
crate::crypto::hash_to_curve(unit.as_bytes())
|
crate::crypto::hash_to_curve(unit.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +36,7 @@ impl NoteCommitment {
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub struct NoteWitness {
|
pub struct NoteWitness {
|
||||||
pub value: u64,
|
pub value: u64,
|
||||||
pub unit: RistrettoPoint,
|
pub unit: Unit,
|
||||||
pub death_constraint: [u8; 32], // death constraint verification key
|
pub death_constraint: [u8; 32], // death constraint verification key
|
||||||
pub state: [u8; 32],
|
pub state: [u8; 32],
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,10 @@ impl NullifierCommitment {
|
||||||
pub fn hex(&self) -> String {
|
pub fn hex(&self) -> String {
|
||||||
hex::encode(self.0)
|
hex::encode(self.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn from_bytes(bytes: [u8; 32]) -> Self {
|
||||||
|
Self(bytes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NullifierNonce {
|
impl NullifierNonce {
|
||||||
|
|
|
@ -8,8 +8,8 @@ use crate::input::{Input, InputWitness};
|
||||||
use crate::merkle;
|
use crate::merkle;
|
||||||
use crate::output::{Output, OutputWitness};
|
use crate::output::{Output, OutputWitness};
|
||||||
|
|
||||||
const MAX_INPUTS: usize = 8;
|
pub const MAX_INPUTS: usize = 8;
|
||||||
const MAX_OUTPUTS: usize = 8;
|
pub const MAX_OUTPUTS: usize = 8;
|
||||||
|
|
||||||
/// The partial transaction commitment couples an input to a partial transaction.
|
/// The partial transaction commitment couples an input to a partial transaction.
|
||||||
/// Prevents partial tx unbundling.
|
/// Prevents partial tx unbundling.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [ "common","host", "methods", "proof_statements", "risc0_proofs"]
|
members = [ "common", "executor", "proof_statements", "risc0_proofs"]
|
||||||
|
|
||||||
# Always optimize; building and running the guest takes much longer without optimization.
|
# Always optimize; building and running the guest takes much longer without optimization.
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
|
|
@ -6,4 +6,6 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
cl = { path = "../../cl/cl" }
|
cl = { path = "../../cl/cl" }
|
||||||
proof_statements = { path = "../proof_statements", package = "goas_proof_statements" }
|
goas_proof_statements = { path = "../proof_statements" }
|
||||||
|
proof_statements = { path = "../../cl/proof_statements" }
|
||||||
|
once_cell = "1"
|
|
@ -1,4 +1,11 @@
|
||||||
use cl::nullifier::{Nullifier, NullifierCommitment};
|
use cl::{
|
||||||
|
balance::Unit,
|
||||||
|
crypto,
|
||||||
|
input::InputWitness,
|
||||||
|
nullifier::{Nullifier, NullifierCommitment},
|
||||||
|
output::OutputWitness,
|
||||||
|
};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
@ -11,6 +18,17 @@ pub const MAX_EVENTS: usize = 1 << 8;
|
||||||
#[derive(Clone, Copy, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||||
pub struct StateCommitment([u8; 32]);
|
pub struct StateCommitment([u8; 32]);
|
||||||
|
|
||||||
|
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?
|
||||||
|
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"));
|
||||||
|
// PLACEHOLDER
|
||||||
|
pub const ZONE_NF_PK: NullifierCommitment = NullifierCommitment::from_bytes([0; 32]);
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct StateWitness {
|
pub struct StateWitness {
|
||||||
pub balances: BTreeMap<u32, u32>,
|
pub balances: BTreeMap<u32, u32>,
|
||||||
|
@ -38,12 +56,8 @@ impl StateWitness {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn included_txs_root(&self) -> [u8; 32] {
|
fn included_txs_root(&self) -> [u8; 32] {
|
||||||
let tx_bytes = Vec::from_iter(
|
// this is a placeholder
|
||||||
self.included_txs
|
let tx_bytes = [vec![0u8; 32]];
|
||||||
.iter()
|
|
||||||
.map(Input::to_bytes)
|
|
||||||
.map(Vec::from_iter),
|
|
||||||
);
|
|
||||||
let tx_merkle_leaves = cl::merkle::padded_leaves(&tx_bytes);
|
let tx_merkle_leaves = cl::merkle::padded_leaves(&tx_bytes);
|
||||||
cl::merkle::root::<MAX_TXS>(tx_merkle_leaves)
|
cl::merkle::root::<MAX_TXS>(tx_merkle_leaves)
|
||||||
}
|
}
|
||||||
|
@ -62,8 +76,8 @@ impl StateWitness {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub struct Withdraw {
|
pub struct Withdraw {
|
||||||
pub from: u32,
|
pub from: AccountId,
|
||||||
pub amount: u32,
|
pub amount: AccountId,
|
||||||
pub to: NullifierCommitment,
|
pub to: NullifierCommitment,
|
||||||
pub nf: Nullifier,
|
pub nf: Nullifier,
|
||||||
}
|
}
|
||||||
|
@ -79,22 +93,30 @@ impl Withdraw {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
/// A deposit of funds into the zone
|
||||||
pub enum Input {
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
Withdraw(Withdraw),
|
pub struct Deposit {
|
||||||
|
/// The note that is used to deposit funds into the zone
|
||||||
|
pub deposit: InputWitness,
|
||||||
|
|
||||||
|
// This zone state note
|
||||||
|
pub zone_note_in: InputWitness,
|
||||||
|
pub zone_note_out: OutputWitness,
|
||||||
|
|
||||||
|
// The zone funds note
|
||||||
|
pub zone_funds_in: InputWitness,
|
||||||
|
pub zone_funds_out: OutputWitness,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub fn to_bytes(&self) -> Vec<u8> {
|
pub enum Input {
|
||||||
match self {
|
Withdraw(Withdraw),
|
||||||
Input::Withdraw(withdraw) => withdraw.to_bytes().to_vec(),
|
Deposit(Deposit),
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Spend(proof_statements::zone_funds::Spend),
|
Spend(goas_proof_statements::zone_funds::Spend),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Event {
|
impl Event {
|
||||||
|
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
||||||
default-run = "host"
|
default-run = "host"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
methods = { path = "../methods" }
|
goas_risc0_proofs = { path = "../risc0_proofs", package = "goas_risc0_proofs" }
|
||||||
risc0-zkvm = { version = "1.0", features = ["prove", "metal"] }
|
risc0-zkvm = { version = "1.0", features = ["prove", "metal"] }
|
||||||
risc0-groth16 = { version = "1.0" }
|
risc0-groth16 = { version = "1.0" }
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|
@ -36,7 +36,7 @@ fn stf_prove_stark(state: StateWitness, inputs: Vec<Input>) {
|
||||||
// This struct contains the receipt along with statistics about execution of the guest
|
// This struct contains the receipt along with statistics about execution of the guest
|
||||||
let opts = risc0_zkvm::ProverOpts::succinct();
|
let opts = risc0_zkvm::ProverOpts::succinct();
|
||||||
let prove_info = prover
|
let prove_info = prover
|
||||||
.prove_with_opts(env, methods::METHOD_ELF, &opts)
|
.prove_with_opts(env, goas_risc0_proofs::ZONE_STATE_ELF, &opts)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
println!("STARK prover time: {:.2?}", start_t.elapsed());
|
println!("STARK prover time: {:.2?}", start_t.elapsed());
|
||||||
|
@ -48,7 +48,7 @@ fn stf_prove_stark(state: StateWitness, inputs: Vec<Input>) {
|
||||||
std::fs::write("proof.stark", bincode::serialize(&receipt).unwrap()).unwrap();
|
std::fs::write("proof.stark", bincode::serialize(&receipt).unwrap()).unwrap();
|
||||||
// The receipt was verified at the end of proving, but the below code is an
|
// The receipt was verified at the end of proving, but the below code is an
|
||||||
// example of how someone else could verify this receipt.
|
// example of how someone else could verify this receipt.
|
||||||
receipt.verify(methods::METHOD_ID).unwrap();
|
receipt.verify(goas_risc0_proofs::ZONE_STATE_ID).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
|
@ -1,10 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "methods"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
risc0-build = { version = "1.0" }
|
|
||||||
|
|
||||||
[package.metadata.risc0]
|
|
||||||
methods = ["guest"]
|
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
risc0_build::embed_methods();
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
use common::*;
|
|
||||||
use proof_statements::zone_funds::Spend;
|
|
||||||
use risc0_zkvm::guest::env;
|
|
||||||
|
|
||||||
fn withdraw(mut state: StateWitness, withdraw: Withdraw) -> StateWitness {
|
|
||||||
state.included_txs.push(Input::Withdraw(withdraw));
|
|
||||||
|
|
||||||
let Withdraw {
|
|
||||||
from,
|
|
||||||
amount,
|
|
||||||
to,
|
|
||||||
nf,
|
|
||||||
} = withdraw;
|
|
||||||
|
|
||||||
let from_balance = state.balances.entry(from).or_insert(0);
|
|
||||||
*from_balance = from.checked_sub(amount).expect("insufficient funds in account");
|
|
||||||
let spend_auth = Spend {
|
|
||||||
amount: amount.into(),
|
|
||||||
to,
|
|
||||||
nf,
|
|
||||||
};
|
|
||||||
|
|
||||||
state.output_events.push(Event::Spend(spend_auth));
|
|
||||||
state
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let inputs: Vec<Input> = env::read();
|
|
||||||
let mut state: StateWitness = env::read();
|
|
||||||
|
|
||||||
for input in inputs {
|
|
||||||
match input {
|
|
||||||
Input::Withdraw(input) => {
|
|
||||||
state = withdraw(state, input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
env::commit(&state.commit());
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
include!(concat!(env!("OUT_DIR"), "/methods.rs"));
|
|
|
@ -22,7 +22,6 @@ impl Spend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// There are two kind of paths
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct SpendFundsPrivate {
|
pub struct SpendFundsPrivate {
|
||||||
/// The note we're spending
|
/// The note we're spending
|
||||||
|
|
|
@ -7,5 +7,5 @@ edition = "2021"
|
||||||
risc0-build = { version = "1.0" }
|
risc0-build = { version = "1.0" }
|
||||||
|
|
||||||
[package.metadata.risc0]
|
[package.metadata.risc0]
|
||||||
methods = ["spend_zone_funds"]
|
methods = ["spend_zone_funds", "zone_state"]
|
||||||
|
|
||||||
|
|
|
@ -60,12 +60,9 @@ fn main() {
|
||||||
out_zone_funds.output.balance_blinding,
|
out_zone_funds.output.balance_blinding,
|
||||||
in_zone_funds.input.balance_blinding
|
in_zone_funds.input.balance_blinding
|
||||||
);
|
);
|
||||||
let mut evolved_nonce = [0; 16];
|
|
||||||
evolved_nonce[..16]
|
|
||||||
.copy_from_slice(&Sha256::digest(&out_zone_funds.output.nonce.as_bytes())[..16]);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
out_zone_funds.output.nonce,
|
out_zone_funds.output.nonce,
|
||||||
NullifierNonce::from_bytes(evolved_nonce)
|
NullifierNonce::from_bytes(Sha256::digest(&out_zone_funds.output.nonce.as_bytes()).into())
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(ptx_root, spent_note.ptx_root());
|
assert_eq!(ptx_root, spent_note.ptx_root());
|
||||||
|
@ -76,8 +73,5 @@ fn main() {
|
||||||
// check the correct recipient is being paid
|
// check the correct recipient is being paid
|
||||||
assert_eq!(spent_note.output.nf_pk, spend_event.to);
|
assert_eq!(spent_note.output.nf_pk, spend_event.to);
|
||||||
|
|
||||||
env::commit(&DeathConstraintPublic {
|
env::commit(&DeathConstraintPublic { ptx_root, nf });
|
||||||
ptx_root,
|
|
||||||
nf,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "method"
|
name = "zone_state"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
@ -14,7 +14,9 @@ serde = { version = "1.0", features = ["derive"] }
|
||||||
bincode = "1"
|
bincode = "1"
|
||||||
common = { path = "../../common" }
|
common = { path = "../../common" }
|
||||||
cl = { path = "../../../cl/cl" }
|
cl = { path = "../../../cl/cl" }
|
||||||
proof_statements = { path = "../../proof_statements", package = "goas_proof_statements" }
|
goas_proof_statements = { path = "../../proof_statements" }
|
||||||
|
proof_statements = { path = "../../../cl/proof_statements" }
|
||||||
|
sha2 = "0.10"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
# Placing these patch statement in the workspace Cargo.toml will add RISC Zero SHA-256 and bigint
|
# Placing these patch statement in the workspace Cargo.toml will add RISC Zero SHA-256 and bigint
|
|
@ -0,0 +1,140 @@
|
||||||
|
use cl::{
|
||||||
|
merkle,
|
||||||
|
nullifier::{Nullifier, NullifierNonce, NullifierSecret},
|
||||||
|
partial_tx::{MAX_INPUTS, MAX_OUTPUTS},
|
||||||
|
PtxRoot,
|
||||||
|
};
|
||||||
|
|
||||||
|
use common::*;
|
||||||
|
use goas_proof_statements::zone_funds::Spend;
|
||||||
|
use proof_statements::death_constraint::DeathConstraintPublic;
|
||||||
|
use risc0_zkvm::guest::env;
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
|
fn withdraw(mut state: StateWitness, withdraw: Withdraw) -> StateWitness {
|
||||||
|
state.included_txs.push(Input::Withdraw(withdraw));
|
||||||
|
|
||||||
|
let Withdraw {
|
||||||
|
from,
|
||||||
|
amount,
|
||||||
|
to,
|
||||||
|
nf,
|
||||||
|
} = withdraw;
|
||||||
|
|
||||||
|
let from_balance = state.balances.entry(from).or_insert(0);
|
||||||
|
*from_balance = from
|
||||||
|
.checked_sub(amount)
|
||||||
|
.expect("insufficient funds in account");
|
||||||
|
let spend_auth = Spend {
|
||||||
|
amount: amount.into(),
|
||||||
|
to,
|
||||||
|
nf,
|
||||||
|
};
|
||||||
|
|
||||||
|
state.output_events.push(Event::Spend(spend_auth));
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deposit(
|
||||||
|
mut state: StateWitness,
|
||||||
|
deposit: Deposit,
|
||||||
|
pub_inputs: DeathConstraintPublic,
|
||||||
|
) -> StateWitness {
|
||||||
|
state.included_txs.push(Input::Deposit(deposit.clone()));
|
||||||
|
|
||||||
|
let Deposit {
|
||||||
|
deposit,
|
||||||
|
zone_note_in,
|
||||||
|
zone_note_out,
|
||||||
|
zone_funds_in,
|
||||||
|
zone_funds_out,
|
||||||
|
} = deposit;
|
||||||
|
|
||||||
|
// 1) Check there are no more input/output notes than expected
|
||||||
|
let inputs = [
|
||||||
|
deposit.commit().to_bytes().to_vec(),
|
||||||
|
zone_note_in.commit().to_bytes().to_vec(),
|
||||||
|
zone_funds_in.commit().to_bytes().to_vec(),
|
||||||
|
];
|
||||||
|
|
||||||
|
let inputs_root = merkle::root(merkle::padded_leaves::<MAX_INPUTS>(&inputs));
|
||||||
|
|
||||||
|
let outputs = [
|
||||||
|
zone_note_out.commit().to_bytes().to_vec(),
|
||||||
|
zone_funds_out.commit().to_bytes().to_vec(),
|
||||||
|
];
|
||||||
|
|
||||||
|
let outputs_root = merkle::root(merkle::padded_leaves::<MAX_OUTPUTS>(&outputs));
|
||||||
|
|
||||||
|
let ptx_root = PtxRoot(merkle::node(inputs_root, outputs_root));
|
||||||
|
assert_eq!(ptx_root, pub_inputs.ptx_root);
|
||||||
|
|
||||||
|
// 2) Check the deposit note is not already under control of the zone
|
||||||
|
assert_ne!(deposit.note.death_constraint, ZONE_FUNDS_VK);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
assert_eq!(deposit.note.unit, *ZONE_UNIT);
|
||||||
|
assert_eq!(zone_funds_in.note.unit, *ZONE_UNIT);
|
||||||
|
assert_eq!(zone_funds_out.note.unit, *ZONE_UNIT);
|
||||||
|
|
||||||
|
let in_sum = deposit.note.value + zone_funds_in.note.value;
|
||||||
|
|
||||||
|
let out_sum = zone_note_out.note.value;
|
||||||
|
|
||||||
|
assert_eq!(out_sum, in_sum, "deposit ptx is unbalanced");
|
||||||
|
|
||||||
|
// 4) Check the zone fund notes are correctly created
|
||||||
|
assert_eq!(zone_funds_in.note.death_constraint, ZONE_FUNDS_VK);
|
||||||
|
assert_eq!(zone_funds_out.note.death_constraint, ZONE_FUNDS_VK);
|
||||||
|
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
|
||||||
|
// nonce is correctly evolved
|
||||||
|
assert_eq!(
|
||||||
|
zone_funds_out.nonce,
|
||||||
|
NullifierNonce::from_bytes(Sha256::digest(&zone_funds_in.nonce.as_bytes()).into())
|
||||||
|
);
|
||||||
|
|
||||||
|
// 5) Check zone state notes are correctly created
|
||||||
|
assert_eq!(
|
||||||
|
zone_note_in.note.death_constraint,
|
||||||
|
zone_note_out.note.death_constraint
|
||||||
|
);
|
||||||
|
assert_eq!(zone_note_in.nf_sk, NullifierSecret::from_bytes([0; 16])); //// there is no secret in the zone state
|
||||||
|
assert_eq!(zone_note_out.nf_pk, zone_note_in.nf_sk.commit()); // the sk is the same
|
||||||
|
assert_eq!(zone_note_in.note.unit, zone_note_out.note.unit);
|
||||||
|
assert_eq!(zone_note_in.note.value, zone_note_out.note.value);
|
||||||
|
// nonce is correctly evolved
|
||||||
|
assert_eq!(
|
||||||
|
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);
|
||||||
|
assert_eq!(nullifier, pub_inputs.nf);
|
||||||
|
|
||||||
|
// 6) We're now ready to do the deposit!
|
||||||
|
let amount = deposit.note.value as u32;
|
||||||
|
let to = AccountId::from_be_bytes(<[u8; 4]>::try_from(&deposit.note.state[0..4]).unwrap());
|
||||||
|
|
||||||
|
let to_balance = state.balances.entry(to).or_insert(0);
|
||||||
|
*to_balance = to_balance
|
||||||
|
.checked_add(amount)
|
||||||
|
.expect("overflow when depositing");
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let public_inputs: DeathConstraintPublic = env::read();
|
||||||
|
let inputs: Vec<Input> = env::read();
|
||||||
|
let mut state: StateWitness = env::read();
|
||||||
|
|
||||||
|
for input in inputs {
|
||||||
|
match input {
|
||||||
|
Input::Withdraw(input) => state = withdraw(state, input),
|
||||||
|
Input::Deposit(input) => state = deposit(state, input, public_inputs),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
env::commit(&state.commit());
|
||||||
|
}
|
Loading…
Reference in New Issue