mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-07 22:04:03 -05:00
smt: simplify ZK gadget. Use root = sparse_merkle_root(pos, path, leaf) instead of the more complicated is_member = sparse_tree_is_member(root, path, pos, leaf)
This commit is contained in:
@@ -5,17 +5,12 @@ constant "SMT" {
|
||||
}
|
||||
|
||||
witness "SMT" {
|
||||
Base root,
|
||||
SparseMerklePath path,
|
||||
Base leaf,
|
||||
}
|
||||
|
||||
circuit "SMT" {
|
||||
is_member = sparse_tree_is_member(root, path, leaf, leaf);
|
||||
|
||||
ONE = witness_base(1);
|
||||
constrain_equal_base(is_member, ONE);
|
||||
|
||||
root = sparse_merkle_root(leaf, path, leaf);
|
||||
constrain_instance(root);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ witness "ProposeInput" {
|
||||
Uint32 leaf_pos,
|
||||
MerklePath coin_path,
|
||||
|
||||
Base null_tree_root,
|
||||
SparseMerklePath null_path,
|
||||
|
||||
Base signature_secret,
|
||||
@@ -45,15 +44,12 @@ circuit "ProposeInput" {
|
||||
# We need this to detect whether the above coin was already spent.
|
||||
# Use a SMT, and show that at this position, the leaf is ZERO
|
||||
ZERO = witness_base(0);
|
||||
ONE = witness_base(1);
|
||||
nullifier = poseidon_hash(coin_secret, coin);
|
||||
is_member = sparse_tree_is_member(
|
||||
null_tree_root, # Expected root
|
||||
null_tree_root = sparse_merkle_root(
|
||||
nullifier, # Position
|
||||
null_path, # Path to root
|
||||
ZERO, # Leaf value
|
||||
nullifier # Position
|
||||
);
|
||||
constrain_equal_base(is_member, ONE);
|
||||
constrain_instance(null_tree_root);
|
||||
|
||||
# Pedersen commitment for coin's coin_value
|
||||
|
||||
@@ -23,7 +23,6 @@ witness "VoteInput" {
|
||||
Uint32 leaf_pos,
|
||||
MerklePath coin_path,
|
||||
|
||||
Base null_tree_root,
|
||||
SparseMerklePath null_path,
|
||||
|
||||
Base signature_secret,
|
||||
@@ -46,15 +45,12 @@ circuit "VoteInput" {
|
||||
# We need this to detect whether the above coin was already spent.
|
||||
# Use a SMT, and show that at this position, the leaf is ZERO
|
||||
ZERO = witness_base(0);
|
||||
ONE = witness_base(1);
|
||||
nullifier = poseidon_hash(coin_secret, coin);
|
||||
is_member = sparse_tree_is_member(
|
||||
null_tree_root, # Expected root
|
||||
null_tree_root = sparse_merkle_root(
|
||||
nullifier, # Position
|
||||
null_path, # Path to root
|
||||
ZERO, # Leaf value
|
||||
nullifier # Position
|
||||
);
|
||||
constrain_equal_base(is_member, ONE);
|
||||
constrain_instance(null_tree_root);
|
||||
|
||||
# Include some secret information in vote nullifier to defeat correlation
|
||||
|
||||
@@ -109,7 +109,6 @@ impl<'a> DaoProposeCall<'a> {
|
||||
Witness::Base(Value::known(gov_token_blind.inner())),
|
||||
Witness::Uint32(Value::known(leaf_pos.try_into().unwrap())),
|
||||
Witness::MerklePath(Value::known(input.merkle_path.clone().try_into().unwrap())),
|
||||
Witness::Base(Value::known(smt_null_root)),
|
||||
Witness::SparseMerklePath(Value::known(smt_null_path.path)),
|
||||
Witness::Base(Value::known(input.signature_secret.inner())),
|
||||
];
|
||||
|
||||
@@ -139,7 +139,6 @@ impl<'a> DaoVoteCall<'a> {
|
||||
Witness::Base(Value::known(gov_token_blind)),
|
||||
Witness::Uint32(Value::known(leaf_pos.try_into().unwrap())),
|
||||
Witness::MerklePath(Value::known(input.merkle_path.clone().try_into().unwrap())),
|
||||
Witness::Base(Value::known(smt_null_root)),
|
||||
Witness::SparseMerklePath(Value::known(smt_null_path.path)),
|
||||
Witness::Base(Value::known(input.signature_secret.inner())),
|
||||
];
|
||||
|
||||
@@ -16,9 +16,8 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use darkfi_money_contract::MONEY_CONTRACT_NULLIFIERS_TREE;
|
||||
use darkfi_sdk::{
|
||||
crypto::{contract_id::MONEY_CONTRACT_ID, pasta_prelude::*, ContractId, PublicKey},
|
||||
crypto::{pasta_prelude::*, ContractId, PublicKey},
|
||||
dark_tree::DarkLeaf,
|
||||
db::{db_contains_key, db_get, db_lookup, db_set},
|
||||
error::{ContractError, ContractResult},
|
||||
@@ -65,7 +64,7 @@ pub(crate) fn dao_vote_get_metadata(
|
||||
return Err(DaoError::ProposalNonexistent.into())
|
||||
};
|
||||
// Get the current votes
|
||||
let mut proposal_metadata: DaoProposalMetadata = deserialize(&data)?;
|
||||
let proposal_metadata: DaoProposalMetadata = deserialize(&data)?;
|
||||
|
||||
// Iterate through inputs
|
||||
for input in ¶ms.inputs {
|
||||
@@ -144,7 +143,6 @@ pub(crate) fn dao_vote_process_instruction(
|
||||
let mut proposal_metadata: DaoProposalMetadata = deserialize(&data)?;
|
||||
|
||||
// Check the Merkle root and nullifiers for the input coins are valid
|
||||
let money_nullifier_db = db_lookup(*MONEY_CONTRACT_ID, MONEY_CONTRACT_NULLIFIERS_TREE)?;
|
||||
let dao_vote_nullifier_db = db_lookup(cid, DAO_CONTRACT_DB_VOTE_NULLIFIERS)?;
|
||||
let mut vote_nullifiers = vec![];
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ pub fn circuit_gas_use(zkbin: &ZkBinary) -> u64 {
|
||||
Opcode::EcGetY => 5,
|
||||
Opcode::PoseidonHash => 20 + 10 * opcode.1.len() as u64,
|
||||
Opcode::MerkleRoot => 10 * MERKLE_DEPTH_ORCHARD as u64,
|
||||
Opcode::SparseTreeIsMember => 10 * SPARSE_MERKLE_DEPTH as u64,
|
||||
Opcode::SparseMerkleRoot => 10 * SPARSE_MERKLE_DEPTH as u64,
|
||||
Opcode::BaseAdd => 15,
|
||||
Opcode::BaseMul => 15,
|
||||
Opcode::BaseSub => 15,
|
||||
|
||||
@@ -33,7 +33,7 @@ use halo2_proofs::{
|
||||
|
||||
use super::{
|
||||
cond_select::{ConditionalSelectChip, ConditionalSelectConfig, NUM_OF_UTILITY_ADVICE_COLUMNS},
|
||||
is_equal::{AssertEqualChip, AssertEqualConfig, IsEqualChip, IsEqualConfig},
|
||||
is_equal::{AssertEqualChip, AssertEqualConfig},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -41,7 +41,6 @@ pub struct PathConfig {
|
||||
s_path: Selector,
|
||||
advices: [Column<Advice>; 2],
|
||||
poseidon_config: PoseidonConfig<Fp, 3, 2>,
|
||||
is_eq_config: IsEqualConfig<Fp>,
|
||||
conditional_select_config: ConditionalSelectConfig<Fp>,
|
||||
assert_equal_config: AssertEqualConfig<Fp>,
|
||||
}
|
||||
@@ -51,10 +50,6 @@ impl PathConfig {
|
||||
PoseidonChip::construct(self.poseidon_config.clone())
|
||||
}
|
||||
|
||||
fn is_eq_chip(&self) -> IsEqualChip<Fp> {
|
||||
IsEqualChip::construct(self.is_eq_config.clone())
|
||||
}
|
||||
|
||||
fn conditional_select_chip(&self) -> ConditionalSelectChip<Fp> {
|
||||
ConditionalSelectChip::construct(self.conditional_select_config.clone())
|
||||
}
|
||||
@@ -99,7 +94,6 @@ impl PathChip {
|
||||
s_path,
|
||||
advices,
|
||||
poseidon_config,
|
||||
is_eq_config: IsEqualChip::configure(meta, utility_advices),
|
||||
conditional_select_config: ConditionalSelectChip::configure(meta, utility_advices),
|
||||
assert_equal_config: AssertEqualChip::configure(
|
||||
meta,
|
||||
@@ -124,10 +118,9 @@ impl PathChip {
|
||||
pub fn check_membership(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<Fp>,
|
||||
root: AssignedCell<Fp, Fp>,
|
||||
leaf: AssignedCell<Fp, Fp>,
|
||||
pos: AssignedCell<Fp, Fp>,
|
||||
path: Value<[Fp; SMT_FP_DEPTH]>,
|
||||
leaf: AssignedCell<Fp, Fp>,
|
||||
) -> Result<AssignedCell<Fp, Fp>, plonk::Error> {
|
||||
let path = path.transpose_array();
|
||||
// Witness values
|
||||
@@ -141,7 +134,7 @@ impl PathChip {
|
||||
let mut witness_path = vec![];
|
||||
for (i, (bit, sibling)) in bits.into_iter().zip(path.into_iter()).enumerate() {
|
||||
let bit = region.assign_advice(
|
||||
|| "witness root",
|
||||
|| "witness pos bit",
|
||||
self.config.advices[0],
|
||||
i,
|
||||
|| bit,
|
||||
@@ -149,7 +142,7 @@ impl PathChip {
|
||||
witness_bits.push(bit);
|
||||
|
||||
let sibling = region.assign_advice(
|
||||
|| "witness root",
|
||||
|| "witness path sibling",
|
||||
self.config.advices[1],
|
||||
i,
|
||||
|| sibling,
|
||||
@@ -158,7 +151,7 @@ impl PathChip {
|
||||
}
|
||||
|
||||
let zero = region.assign_advice(
|
||||
|| "witness one",
|
||||
|| "witness zero",
|
||||
self.config.advices[0],
|
||||
SMT_FP_DEPTH,
|
||||
|| Value::known(Fp::ZERO),
|
||||
@@ -171,7 +164,6 @@ impl PathChip {
|
||||
assert_eq!(bits.len(), path.len());
|
||||
assert_eq!(bits.len(), SMT_FP_DEPTH);
|
||||
|
||||
let iseq_chip = self.config.is_eq_chip();
|
||||
let condselect_chip = self.config.conditional_select_chip();
|
||||
let asserteq_chip = self.config.assert_eq_chip();
|
||||
|
||||
@@ -235,7 +227,8 @@ impl PathChip {
|
||||
|
||||
asserteq_chip.assert_equal(layouter, current_path, pos)?;
|
||||
|
||||
iseq_chip.is_eq_with_output(layouter, current_node, root)
|
||||
let root = current_node;
|
||||
Ok(root)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,9 +240,9 @@ mod tests {
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
struct TestCircuit {
|
||||
root: Value<Fp>,
|
||||
path: Value<[Fp; SMT_FP_DEPTH]>,
|
||||
leaf: Value<Fp>,
|
||||
root: Value<Fp>,
|
||||
}
|
||||
|
||||
impl Circuit<Fp> for TestCircuit {
|
||||
@@ -302,43 +295,31 @@ mod tests {
|
||||
let assert_eq_chip = config.assert_eq_chip();
|
||||
|
||||
// Witness values
|
||||
let (root, leaf, one) = layouter.assign_region(
|
||||
let (leaf, root) = layouter.assign_region(
|
||||
|| "witness",
|
||||
|mut region| {
|
||||
let root = region.assign_advice(
|
||||
|| "witness root",
|
||||
config.advices[0],
|
||||
0,
|
||||
|| self.root,
|
||||
)?;
|
||||
|
||||
let leaf = region.assign_advice(
|
||||
|| "witness leaf",
|
||||
config.advices[1],
|
||||
config.advices[0],
|
||||
0,
|
||||
|| self.leaf,
|
||||
)?;
|
||||
|
||||
let one = region.assign_advice(
|
||||
|| "witness one",
|
||||
let root = region.assign_advice(
|
||||
|| "witness root",
|
||||
config.advices[1],
|
||||
1,
|
||||
|| Value::known(Fp::ONE),
|
||||
0,
|
||||
|| self.root,
|
||||
)?;
|
||||
region.constrain_constant(one.cell(), Fp::ONE)?;
|
||||
|
||||
Ok((root, leaf, one))
|
||||
Ok((leaf, root))
|
||||
},
|
||||
)?;
|
||||
|
||||
let is_valid = path_chip.check_membership(
|
||||
&mut layouter,
|
||||
root,
|
||||
leaf.clone(),
|
||||
leaf.clone(),
|
||||
self.path,
|
||||
)?;
|
||||
assert_eq_chip.assert_equal(&mut layouter, is_valid, one)?;
|
||||
let calc_root =
|
||||
path_chip.check_membership(&mut layouter, leaf.clone(), self.path, leaf.clone())?;
|
||||
// Normally we just reveal it as a public input.
|
||||
// But I'm too lazy to make a separate config for this unit test so
|
||||
// do this instead.
|
||||
assert_eq_chip.assert_equal(&mut layouter, calc_root, root)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -365,9 +346,9 @@ mod tests {
|
||||
assert!(path.verify(&root, &leaf, &pos));
|
||||
|
||||
let circuit = TestCircuit {
|
||||
root: Value::known(root),
|
||||
path: Value::known(path.path),
|
||||
leaf: Value::known(leaf),
|
||||
root: Value::known(root),
|
||||
};
|
||||
|
||||
const K: u32 = 14;
|
||||
|
||||
12
src/zk/vm.rs
12
src/zk/vm.rs
@@ -999,20 +999,18 @@ impl Circuit<pallas::Base> for ZkCircuit {
|
||||
heap.push(HeapVar::Base(root));
|
||||
}
|
||||
|
||||
Opcode::SparseTreeIsMember => {
|
||||
Opcode::SparseMerkleRoot => {
|
||||
trace!(target: "zk::vm", "Executing `SparseTreeIsMember{:?}` opcode", opcode.1);
|
||||
let args = &opcode.1;
|
||||
|
||||
let root = heap[args[0].1].clone().try_into()?;
|
||||
let pos = heap[args[0].1].clone().try_into()?;
|
||||
let path: Value<[Fp; SMT_FP_DEPTH]> = heap[args[1].1].clone().try_into()?;
|
||||
let leaf = heap[args[2].1].clone().try_into()?;
|
||||
let pos = heap[args[3].1].clone().try_into()?;
|
||||
|
||||
let is_member =
|
||||
smt_chip.check_membership(&mut layouter, root, leaf, pos, path)?;
|
||||
let root = smt_chip.check_membership(&mut layouter, pos, path, leaf)?;
|
||||
|
||||
self.tracer.push_base(&is_member);
|
||||
heap.push(HeapVar::Base(is_member));
|
||||
self.tracer.push_base(&root);
|
||||
heap.push(HeapVar::Base(root));
|
||||
}
|
||||
|
||||
Opcode::BaseAdd => {
|
||||
|
||||
@@ -52,8 +52,8 @@ pub enum Opcode {
|
||||
/// Calculate Merkle root, given a position, Merkle path, and an element
|
||||
MerkleRoot = 0x20,
|
||||
|
||||
/// Check for leaf membership in sparse merkle tree
|
||||
SparseTreeIsMember = 0x21,
|
||||
/// Calculate sparse Merkle root, given the position, path and a member
|
||||
SparseMerkleRoot = 0x21,
|
||||
|
||||
/// Base field element addition
|
||||
BaseAdd = 0x30,
|
||||
@@ -112,7 +112,7 @@ impl Opcode {
|
||||
"ec_get_y" => Some(Self::EcGetY),
|
||||
"poseidon_hash" => Some(Self::PoseidonHash),
|
||||
"merkle_root" => Some(Self::MerkleRoot),
|
||||
"sparse_tree_is_member" => Some(Self::SparseTreeIsMember),
|
||||
"sparse_merkle_root" => Some(Self::SparseMerkleRoot),
|
||||
"base_add" => Some(Self::BaseAdd),
|
||||
"base_mul" => Some(Self::BaseMul),
|
||||
"base_sub" => Some(Self::BaseSub),
|
||||
@@ -142,7 +142,7 @@ impl Opcode {
|
||||
0x09 => Some(Self::EcGetY),
|
||||
0x10 => Some(Self::PoseidonHash),
|
||||
0x20 => Some(Self::MerkleRoot),
|
||||
0x21 => Some(Self::SparseTreeIsMember),
|
||||
0x21 => Some(Self::SparseMerkleRoot),
|
||||
0x30 => Some(Self::BaseAdd),
|
||||
0x31 => Some(Self::BaseMul),
|
||||
0x32 => Some(Self::BaseSub),
|
||||
@@ -173,7 +173,7 @@ impl Opcode {
|
||||
Self::EcGetY => "ec_get_y",
|
||||
Self::PoseidonHash => "poseidon_hash",
|
||||
Self::MerkleRoot => "merkle_root",
|
||||
Self::SparseTreeIsMember => "sparse_tree_is_member",
|
||||
Self::SparseMerkleRoot => "sparse_merkle_root",
|
||||
Self::BaseAdd => "base_add",
|
||||
Self::BaseMul => "base_mul",
|
||||
Self::BaseSub => "base_sub",
|
||||
@@ -223,10 +223,9 @@ impl Opcode {
|
||||
(vec![VarType::Base], vec![VarType::Uint32, VarType::MerklePath, VarType::Base])
|
||||
}
|
||||
|
||||
Opcode::SparseTreeIsMember => (
|
||||
vec![VarType::Base],
|
||||
vec![VarType::Base, VarType::SparseMerklePath, VarType::Base, VarType::Base],
|
||||
),
|
||||
Opcode::SparseMerkleRoot => {
|
||||
(vec![VarType::Base], vec![VarType::Base, VarType::SparseMerklePath, VarType::Base])
|
||||
}
|
||||
|
||||
Opcode::BaseAdd => (vec![VarType::Base], vec![VarType::Base, VarType::Base]),
|
||||
|
||||
|
||||
@@ -55,11 +55,8 @@ fn zkvm_smt() -> Result<()> {
|
||||
assert!(path.verify(&root, &leaf, &pos));
|
||||
|
||||
// Values for the proof
|
||||
let prover_witnesses = vec![
|
||||
Witness::Base(Value::known(root)),
|
||||
Witness::SparseMerklePath(Value::known(path.path)),
|
||||
Witness::Base(Value::known(leaf)),
|
||||
];
|
||||
let prover_witnesses =
|
||||
vec![Witness::SparseMerklePath(Value::known(path.path)), Witness::Base(Value::known(leaf))];
|
||||
|
||||
let public_inputs = vec![root];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user