#![cfg_attr(not(feature = "std"), no_std)]
pub mod traits;
pub mod utils;
#[cfg(test)]
pub mod mock;
#[cfg(test)]
pub mod tests;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub mod weights;
use bulletproofs::{
r1cs::{R1CSProof, Verifier},
BulletproofGens, PedersenGens,
};
use bulletproofs_gadgets::{
fixed_deposit_tree::mixer_verif_gadget,
poseidon::{
allocate_statics_for_verifier,
builder::{Poseidon, PoseidonBuilder},
PoseidonSbox, Poseidon_hash_2,
},
smt::gen_zero_tree,
utils::AllocatedScalar,
};
use codec::{Decode, Encode};
use curve25519_dalek::scalar::Scalar;
use frame_support::{dispatch, ensure, traits::Get, weights::Weight, Parameter};
use frame_system::ensure_signed;
use merlin::Transcript;
use rand_core::OsRng;
use sp_runtime::traits::{AtLeast32Bit, One};
use sp_std::prelude::*;
pub use traits::Group;
use utils::{
keys::{Commitment, ScalarData},
permissions::ensure_admin,
};
use weights::WeightInfo;
pub fn default_hasher() -> Poseidon {
let width = 6;
let bp_gens = BulletproofGens::new(16400, 1);
PoseidonBuilder::new(width)
.bulletproof_gens(bp_gens)
.sbox(PoseidonSbox::Exponentiation3)
.build()
}
pub use pallet::*;
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config {
type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;
type GroupId: Encode + Decode + Parameter + AtLeast32Bit + Default + Copy;
type MaxTreeDepth: Get<u8>;
type CacheBlockLength: Get<Self::BlockNumber>;
type WeightInfo: WeightInfo;
}
#[pallet::error]
pub enum Error<T> {
NoneValue,
ExceedsMaxLeaves,
GroupDoesntExist,
InvalidMembershipProof,
InvalidPathLength,
InvalidPrivateInputs,
AlreadyUsedNullifier,
ZkVericationFailed,
InvalidZkProof,
InvalidTreeDepth,
InvalidMerkleRoot,
ManagerIsRequired,
ManagerDoesntExist,
}
#[pallet::event]
#[pallet::generate_deposit(pub(crate) fn deposit_event)]
pub enum Event<T: Config> {
NewMember(T::GroupId, T::AccountId, Vec<ScalarData>),
}
#[pallet::storage]
#[pallet::getter(fn next_group_id)]
pub type NextGroupId<T: Config> = StorageValue<_, T::GroupId, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn groups)]
pub type Groups<T: Config> = StorageMap<_, Blake2_128Concat, T::GroupId, Option<GroupTree>, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn cached_roots)]
pub type CachedRoots<T: Config> = StorageDoubleMap<
_,
Blake2_128Concat,
T::BlockNumber,
Blake2_128Concat,
T::GroupId,
Vec<ScalarData>,
ValueQuery,
>;
#[pallet::storage]
#[pallet::getter(fn get_manager)]
pub type Managers<T: Config> = StorageMap<_, Blake2_128Concat, T::GroupId, Option<Manager<T>>, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn lowest_cached_block)]
pub type LowestCachedBlock<T: Config> = StorageValue<_, T::BlockNumber, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn highest_cached_block)]
pub type HighestCachedBlock<T: Config> = StorageValue<_, T::BlockNumber, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn used_nullifiers)]
pub type UsedNullifiers<T: Config> = StorageMap<_, Blake2_128Concat, (T::GroupId, ScalarData), bool, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn stopped)]
pub type Stopped<T: Config> = StorageMap<_, Blake2_128Concat, T::GroupId, bool, ValueQuery>;
#[pallet::pallet]
pub struct Pallet<T>(PhantomData<T>);
#[pallet::hooks]
impl<T: Config> Hooks<T::BlockNumber> for Pallet<T> {
fn on_initialize(_n: T::BlockNumber) -> Weight {
<T as Config>::WeightInfo::on_finalize()
}
fn on_finalize(n: T::BlockNumber) {
if HighestCachedBlock::<T>::get() < n {
HighestCachedBlock::<T>::set(n);
}
if LowestCachedBlock::<T>::get() < One::one() {
LowestCachedBlock::<T>::set(n);
}
if HighestCachedBlock::<T>::get() > T::CacheBlockLength::get() {
if HighestCachedBlock::<T>::get() - T::CacheBlockLength::get() >= LowestCachedBlock::<T>::get() {
CachedRoots::<T>::remove_prefix(LowestCachedBlock::<T>::get());
LowestCachedBlock::<T>::set(LowestCachedBlock::<T>::get() + One::one());
}
}
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(<T as Config>::WeightInfo::create_group(_depth.map_or(T::MaxTreeDepth::get() as u32, |x| x as u32)))]
pub fn create_group(origin: OriginFor<T>, r_is_mgr: bool, _depth: Option<u8>) -> DispatchResultWithPostInfo {
let sender = ensure_signed(origin)?;
let depth = match _depth {
Some(d) => d,
None => T::MaxTreeDepth::get(),
};
let _ = <Self as Group<_, _, _>>::create_group(sender, r_is_mgr, depth)?;
Ok(().into())
}
#[pallet::weight(<T as Config>::WeightInfo::set_manager_required())]
pub fn set_manager_required(
origin: OriginFor<T>,
group_id: T::GroupId,
manager_required: bool,
) -> DispatchResultWithPostInfo {
let sender = ensure_signed(origin)?;
<Self as Group<_, _, _>>::set_manager_required(sender, group_id, manager_required)?;
Ok(().into())
}
#[pallet::weight(<T as Config>::WeightInfo::set_manager())]
pub fn set_manager(
origin: OriginFor<T>,
group_id: T::GroupId,
new_manager: T::AccountId,
) -> DispatchResultWithPostInfo {
let manager_data = Managers::<T>::get(group_id)
.ok_or(Error::<T>::ManagerDoesntExist)
.unwrap();
ensure_admin(origin, &manager_data.account_id)?;
<Self as Group<_, _, _>>::set_manager(manager_data.account_id, group_id, new_manager)?;
Ok(().into())
}
#[pallet::weight(<T as Config>::WeightInfo::set_stopped())]
pub fn set_stopped(origin: OriginFor<T>, group_id: T::GroupId, stopped: bool) -> DispatchResultWithPostInfo {
let manager_data = Managers::<T>::get(group_id)
.ok_or(Error::<T>::ManagerDoesntExist)
.unwrap();
ensure_admin(origin, &manager_data.account_id)?;
<Self as Group<_, _, _>>::set_stopped(manager_data.account_id, group_id, stopped)?;
Ok(().into())
}
#[pallet::weight(<T as Config>::WeightInfo::add_members(members.len() as u32))]
pub fn add_members(
origin: OriginFor<T>,
group_id: T::GroupId,
members: Vec<ScalarData>,
) -> DispatchResultWithPostInfo {
let sender = ensure_signed(origin)?;
<Self as Group<_, _, _>>::add_members(sender, group_id, members)?;
Ok(().into())
}
#[pallet::weight(<T as Config>::WeightInfo::verify_path(path.len() as u32))]
pub fn verify(
origin: OriginFor<T>,
group_id: T::GroupId,
leaf: ScalarData,
path: Vec<(bool, ScalarData)>,
) -> DispatchResultWithPostInfo {
let _sender = ensure_signed(origin)?;
<Self as Group<_, _, _>>::verify(group_id, leaf, path)?;
Ok(().into())
}
}
}
#[derive(Clone, Encode, Decode, PartialEq)]
pub struct Manager<T: Config> {
pub account_id: T::AccountId,
pub required: bool,
}
impl<T: Config> Manager<T> {
pub fn new(account_id: T::AccountId, required: bool) -> Self {
Self { account_id, required }
}
}
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Clone, Encode, Decode, PartialEq)]
pub struct GroupTree {
pub leaf_count: u32,
pub max_leaves: u32,
pub depth: u8,
pub root_hash: ScalarData,
pub edge_nodes: Vec<ScalarData>,
}
impl GroupTree {
pub fn new<T: Config>(depth: u8) -> Self {
let zero_tree = gen_zero_tree(6, &PoseidonSbox::Exponentiation3);
let init_edges: Vec<ScalarData> = zero_tree[0..depth as usize]
.iter()
.map(|x| ScalarData::from(*x))
.collect();
let init_root = ScalarData::from(zero_tree[depth as usize]);
Self {
root_hash: init_root,
leaf_count: 0,
depth,
max_leaves: u32::MAX >> (T::MaxTreeDepth::get() - depth),
edge_nodes: init_edges,
}
}
}
impl<T: Config> Group<T::AccountId, T::BlockNumber, T::GroupId> for Pallet<T> {
fn create_group(
sender: T::AccountId,
is_manager_required: bool,
depth: u8,
) -> Result<T::GroupId, dispatch::DispatchError> {
ensure!(
depth <= T::MaxTreeDepth::get() && depth > 0,
Error::<T>::InvalidTreeDepth
);
let group_id = Self::next_group_id();
NextGroupId::<T>::mutate(|id| *id += One::one());
let mtree = GroupTree::new::<T>(depth);
Groups::<T>::insert(group_id, Some(mtree));
let manager = Manager::<T>::new(sender, is_manager_required);
Managers::<T>::insert(group_id, Some(manager));
Ok(group_id)
}
fn set_stopped(sender: T::AccountId, id: T::GroupId, stopped: bool) -> Result<(), dispatch::DispatchError> {
let manager_data = Managers::<T>::get(id).ok_or(Error::<T>::ManagerDoesntExist).unwrap();
ensure!(sender == manager_data.account_id, Error::<T>::ManagerIsRequired);
Stopped::<T>::insert(id, stopped);
Ok(())
}
fn set_manager_required(
sender: T::AccountId,
id: T::GroupId,
manager_required: bool,
) -> Result<(), dispatch::DispatchError> {
let mut manager_data = Managers::<T>::get(id).ok_or(Error::<T>::ManagerDoesntExist).unwrap();
ensure!(sender == manager_data.account_id, Error::<T>::ManagerIsRequired);
manager_data.required = manager_required;
Managers::<T>::insert(id, Some(manager_data));
Ok(())
}
fn set_manager(
sender: T::AccountId,
id: T::GroupId,
new_manager: T::AccountId,
) -> Result<(), dispatch::DispatchError> {
let mut manager_data = Managers::<T>::get(id).ok_or(Error::<T>::ManagerDoesntExist).unwrap();
ensure!(sender == manager_data.account_id, Error::<T>::ManagerIsRequired);
manager_data.account_id = new_manager;
Managers::<T>::insert(id, Some(manager_data));
Ok(())
}
fn add_members(
sender: T::AccountId,
id: T::GroupId,
members: Vec<ScalarData>,
) -> Result<(), dispatch::DispatchError> {
let mut tree = Groups::<T>::get(id).ok_or(Error::<T>::GroupDoesntExist).unwrap();
let manager_data = Managers::<T>::get(id).ok_or(Error::<T>::ManagerDoesntExist).unwrap();
ensure!(
Self::is_manager_required(sender.clone(), &manager_data),
Error::<T>::ManagerIsRequired
);
let num_points = members.len() as u32;
ensure!(
tree.leaf_count + num_points <= tree.max_leaves,
Error::<T>::ExceedsMaxLeaves
);
let h = default_hasher();
let zero_tree = gen_zero_tree(h.width, &h.sbox);
for data in &members {
Self::add_leaf(&mut tree, *data, &zero_tree, &h);
}
let block_number: T::BlockNumber = <frame_system::Module<T>>::block_number();
CachedRoots::<T>::append(block_number, id, tree.root_hash);
Groups::<T>::insert(id, Some(tree));
Self::deposit_event(Event::NewMember(id, sender, members));
Ok(())
}
fn add_nullifier(
sender: T::AccountId,
id: T::GroupId,
nullifier_hash: ScalarData,
) -> Result<(), dispatch::DispatchError> {
let manager_data = Managers::<T>::get(id).ok_or(Error::<T>::ManagerDoesntExist).unwrap();
ensure!(
Self::is_manager_required(sender.clone(), &manager_data),
Error::<T>::ManagerIsRequired
);
UsedNullifiers::<T>::insert((id, nullifier_hash), true);
Ok(())
}
fn has_used_nullifier(id: T::GroupId, nullifier: ScalarData) -> Result<(), dispatch::DispatchError> {
let _ = Groups::<T>::get(id).ok_or(Error::<T>::GroupDoesntExist).unwrap();
ensure!(
!UsedNullifiers::<T>::contains_key((id, nullifier)),
Error::<T>::AlreadyUsedNullifier
);
Ok(())
}
fn verify(id: T::GroupId, leaf: ScalarData, path: Vec<(bool, ScalarData)>) -> Result<(), dispatch::DispatchError> {
let tree = Groups::<T>::get(id).ok_or(Error::<T>::GroupDoesntExist).unwrap();
ensure!(tree.edge_nodes.len() == path.len(), Error::<T>::InvalidPathLength);
let h = default_hasher();
let mut hash = leaf.0;
for (is_right, node) in path {
hash = match is_right {
true => Poseidon_hash_2(hash, node.0, &h),
false => Poseidon_hash_2(node.0, hash, &h),
}
}
ensure!(hash == tree.root_hash.0, Error::<T>::InvalidMembershipProof);
Ok(())
}
fn verify_zk_membership_proof(
group_id: T::GroupId,
cached_block: T::BlockNumber,
cached_root: ScalarData,
comms: Vec<Commitment>,
nullifier_hash: ScalarData,
proof_bytes: Vec<u8>,
leaf_index_commitments: Vec<Commitment>,
proof_commitments: Vec<Commitment>,
recipient: ScalarData,
relayer: ScalarData,
) -> Result<(), dispatch::DispatchError> {
let tree = Groups::<T>::get(group_id).ok_or(Error::<T>::GroupDoesntExist).unwrap();
ensure!(
tree.edge_nodes.len() == proof_commitments.len(),
Error::<T>::InvalidPathLength
);
let old_roots = Self::cached_roots(cached_block, group_id);
ensure!(
old_roots.iter().any(|r| *r == cached_root),
Error::<T>::InvalidMerkleRoot
);
let pc_gens = PedersenGens::default();
<Self as Group<_, _, _>>::verify_zk(
pc_gens,
cached_root,
tree.depth,
comms,
nullifier_hash,
proof_bytes,
leaf_index_commitments,
proof_commitments,
recipient,
relayer,
)
}
fn verify_zk(
pc_gens: PedersenGens,
m_root: ScalarData,
depth: u8,
comms: Vec<Commitment>,
nullifier_hash: ScalarData,
proof_bytes: Vec<u8>,
leaf_index_commitments: Vec<Commitment>,
proof_commitments: Vec<Commitment>,
recipient: ScalarData,
relayer: ScalarData,
) -> Result<(), dispatch::DispatchError> {
let label = b"zk_membership_proof";
let h = default_hasher();
let mut verifier_transcript = Transcript::new(label);
let mut verifier = Verifier::new(&mut verifier_transcript);
ensure!(comms.len() == 3, Error::<T>::InvalidPrivateInputs);
let r_val = verifier.commit(comms[0].0);
let r_alloc = AllocatedScalar {
variable: r_val,
assignment: None,
};
let nullifier_val = verifier.commit(comms[1].0);
let nullifier_alloc = AllocatedScalar {
variable: nullifier_val,
assignment: None,
};
let var_leaf = verifier.commit(comms[2].0);
let leaf_alloc_scalar = AllocatedScalar {
variable: var_leaf,
assignment: None,
};
let mut leaf_index_alloc_scalars = vec![];
for l in leaf_index_commitments {
let v = verifier.commit(l.0);
leaf_index_alloc_scalars.push(AllocatedScalar {
variable: v,
assignment: None,
});
}
let mut proof_alloc_scalars = vec![];
for p in proof_commitments {
let v = verifier.commit(p.0);
proof_alloc_scalars.push(AllocatedScalar {
variable: v,
assignment: None,
});
}
let num_statics = 4;
let statics = allocate_statics_for_verifier(&mut verifier, num_statics, &pc_gens);
let gadget_res = mixer_verif_gadget(
&mut verifier,
&recipient.to_scalar(),
&relayer.to_scalar(),
depth as usize,
&m_root.0,
&nullifier_hash.0,
r_alloc,
nullifier_alloc,
leaf_alloc_scalar,
leaf_index_alloc_scalars,
proof_alloc_scalars,
statics,
&h,
);
ensure!(gadget_res.is_ok(), Error::<T>::InvalidZkProof);
let proof = R1CSProof::from_bytes(&proof_bytes);
ensure!(proof.is_ok(), Error::<T>::InvalidZkProof);
let proof = proof.unwrap();
let mut rng = OsRng::default();
let verify_res = verifier.verify_with_rng(&proof, &h.pc_gens, &h.bp_gens, &mut rng);
ensure!(verify_res.is_ok(), Error::<T>::ZkVericationFailed);
Ok(())
}
}
impl<T: Config> Pallet<T> {
pub fn get_cache(group_id: T::GroupId, block_number: T::BlockNumber) -> Vec<ScalarData> {
Self::cached_roots(block_number, group_id)
}
pub fn get_merkle_root(group_id: T::GroupId) -> Result<ScalarData, dispatch::DispatchError> {
let group = Self::get_group(group_id)?;
Ok(group.root_hash)
}
pub fn add_root_to_cache(
group_id: T::GroupId,
block_number: T::BlockNumber,
) -> Result<(), dispatch::DispatchError> {
let root = Self::get_merkle_root(group_id)?;
CachedRoots::<T>::append(block_number, group_id, root);
Ok(())
}
pub fn get_group(group_id: T::GroupId) -> Result<GroupTree, dispatch::DispatchError> {
let tree = Groups::<T>::get(group_id).ok_or(Error::<T>::GroupDoesntExist).unwrap();
Ok(tree)
}
pub fn is_manager_required(sender: T::AccountId, manager: &Manager<T>) -> bool {
if manager.required {
return sender == manager.account_id;
} else {
return true;
}
}
pub fn add_leaf(tree: &mut GroupTree, data: ScalarData, zero_tree: &Vec<[u8; 32]>, h: &Poseidon) {
let mut edge_index = tree.leaf_count;
let mut hash = data.0;
for i in 0..tree.edge_nodes.len() {
hash = if edge_index % 2 == 0 {
tree.edge_nodes[i] = ScalarData(hash);
let zero_h = Scalar::from_bytes_mod_order(zero_tree[i]);
Poseidon_hash_2(hash, zero_h, h)
} else {
Poseidon_hash_2(tree.edge_nodes[i].0, hash, h)
};
edge_index /= 2;
}
tree.leaf_count += 1;
tree.root_hash = ScalarData(hash);
}
}