Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
[workspace]
members = ["doxa-client","doxa-server", "doxa-trees", "doxa-e2e", "doxa-subpool-database", "doxa-subpool-operator", "doxa-state-sync"]
members = [
"doxa-client",
"doxa-server",
"doxa-trees",
"doxa-e2e",
"doxa-subpool-database",
"doxa-subpool-operator",
"doxa-state-sync",
]
resolver = "2"

[workspace.dependencies]
Expand Down Expand Up @@ -30,8 +38,15 @@ itertools = "0.14.0"
primitive-types = "0.14.0"
tiny-keccak = { version = "2.0", features = ["keccak"] }
hashbrown = "0.14"
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "postgres", "macros", "migrate", "json", "chrono"] }
chrono = { version = "0.4", features = ["serde"] }
sqlx = { version = "0.8", features = [
"runtime-tokio-rustls",
"postgres",
"macros",
"migrate",
"json",
"chrono",
] }
chrono = { version = "0.4", features = ["serde"] }
tower-http = { version = "0.6", features = ["cors", "trace"] }

# Uncomment to use local plonky2 checkout instead of git (for debugging).
Expand Down
10 changes: 9 additions & 1 deletion doxa-client/src/plonky2_gadgets/merkle.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use plonky2::{
hash::hash_types::{HashOutTarget, RichField},
iop::{
target::BoolTarget,
target::{BoolTarget, Target},
witness::{PartialWitness, WitnessWrite},
},
plonk::circuit_builder::CircuitBuilder,
Expand Down Expand Up @@ -56,6 +56,14 @@ pub struct MerkleRootTarget {
}

impl MerkleRootTarget {
/// Compose `bits` into a single Target encoding the leaf's position in the tree
pub fn pos<F, const D: usize>(&self, builder: &mut CircuitBuilder<F, D>) -> Target
where
F: RichField + Extendable<D>,
{
builder.le_sum(self.bits.iter())
}

pub(crate) fn set_witness(&self, pw: &mut PartialWitness<F>, proof: &MerkleProof<HashOutput>) {
set_merkle_path_witness(pw, &self.siblings, &self.bits, proof);
}
Expand Down
11 changes: 0 additions & 11 deletions doxa-client/src/plonky2_gadgets/priv_tx/builder/built_priv_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,11 +433,6 @@ impl BuiltPrivTx {
// Input note
t.private.inotes[i].set_witness(pw, inote);
t.private.inotes_nct_merkle[i].set_witness(pw, proof);
pw.set_target(
t.private.inotes_pos[i],
F::from_canonical_u64(proof.pos as u64),
)
.unwrap();
pw.set_bool_target(t.private.inotes_isactive[i], true)
.unwrap();
t.private.dinotes[i].set(pw, Default::default()); // active slot — value unused but target must be set
Expand All @@ -462,16 +457,10 @@ impl BuiltPrivTx {
let proof = &self.inotes_nct_proofs[j];
t.private.inotes[slot].set_witness(pw, note);
t.private.inotes_nct_merkle[slot].set_witness(pw, proof);
pw.set_target(
t.private.inotes_pos[slot],
F::from_canonical_u64(proof.pos as u64),
)
.unwrap();
pw.set_bool_target(t.private.inotes_isactive[slot], true)
.unwrap();
t.private.dinotes[slot].set(pw, Default::default()); // active slot — value unused
} else {
pw.set_target(t.private.inotes_pos[slot], F::ZERO).unwrap();
pw.set_bool_target(t.private.inotes_isactive[slot], false)
.unwrap();
t.private.dinotes[slot].set(pw, self.dinotes[j - n_in]);
Expand Down
22 changes: 17 additions & 5 deletions doxa-client/src/plonky2_gadgets/priv_tx/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ where
let is_update_auth = builder.add_virtual_bool_target_safe();
let is_priv_tx = builder.add_virtual_bool_target_safe();

// Exactly one kind flag is set for a real tx; all zero for a dummy.
let kind_sum = builder.add_many([
is_fresh_acc.target,
is_update_auth.target,
is_priv_tx.target,
]);
builder.connect(kind_sum, not_fake_tx.target);

let root = StateRootTarget(builder.add_virtual_hash());
let mainpool_config_root = MainPoolConfigRootTarget(builder.add_virtual_hash());

Expand Down Expand Up @@ -155,12 +163,9 @@ where
// Inactive slots are filled with dummy values; all are padded to NOTE_BATCH.
let inotes: [StandardNoteTarget; NOTE_BATCH] =
core::array::from_fn(|_| builder.add_virtual_note_target());
let inotes_pos: [Target; NOTE_BATCH] = core::array::from_fn(|_| builder.add_virtual_target());
let inotes_isactive: [BoolTarget; NOTE_BATCH] =
core::array::from_fn(|_| builder.add_virtual_bool_target_safe());
let inotes_comm = core::array::from_fn(|i| builder.derive_note_commitment(inotes[i]));
let inotes_null: [NoteNullifierTarget; NOTE_BATCH] =
core::array::from_fn(|i| builder.derive_note_nullifier(inotes_comm[i], inotes_pos[i], nk));

let onotes: [StandardNoteTarget; NOTE_BATCH] =
core::array::from_fn(|_| builder.add_virtual_note_target());
Expand Down Expand Up @@ -203,6 +208,13 @@ where
root,
);

// Step 9b: Bind note positions to merkle proofs
let inotes_pos: [Target; NOTE_BATCH] =
core::array::from_fn(|i| inotes_mrkltrgt[i].pos(builder));
let inotes_null: [NoteNullifierTarget; NOTE_BATCH] = core::array::from_fn(|i| {
builder.derive_note_nullifier(inotes_comm[i], inotes_pos[i], nk)
});

// Step 10: Balance invariant — assets are conserved across accounts and notes.
// accin_amt + Σ(active inote amounts) == accout_amt + Σ(active onote amounts)
builder.assert_balance_invariant(
Expand Down Expand Up @@ -276,7 +288,7 @@ where
accin_null,
accout_comm,
inotes_null: effective_inotes_null,
onotes_comm: donotes_comm,
onotes_comm: derived_onotes_comm,
};

public_targets.register(builder);
Expand Down Expand Up @@ -317,7 +329,7 @@ pub struct PrivTxCircuit {
pub circuit_data: doxa_utils::CircuitDataNative,
pub targets: TxCircuitTargets,
}

/// Build the priv_tx circuit using `HashOutput` as the Merkle hasher.
pub fn build_priv_tx_circuit() -> PrivTxCircuit {
use plonky2::plonk::circuit_data::CircuitConfig;
Expand Down
7 changes: 3 additions & 4 deletions doxa-client/src/plonky2_gadgets/priv_tx/circuit_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -640,10 +640,9 @@ where
let expected_nonce = self.add(accin.nonce, one);
self.connect(accout.nonce, expected_nonce);

// acc_ast_root is immutable for FreshAccTx and UpdateAuthTx; PrivTx may update it
//
// not_spend = !is_priv_tx = is_fresh_acc | is_update_auth | (reject pairs), because we
// constrain elsewhere that only 1 flag of the set is set to true at any time
// acc_ast_root is immutable for FreshAccTx and UpdateAuthTx; PrivTx may update it.
// One-hot kind flags (enforced in priv_tx_circuit) make
// !is_priv_tx == (is_fresh_acc | is_update_auth) for real txs.
let not_spend = self.not(is_priv_tx);
for i in 0..HASH_SIZE {
// TODO: use is_fresh_acc | is_update_auth here instead of not_spend
Expand Down
Loading