Add great boy example (#48)
* Progress towards mock MainPod verification * add MockMainPod.pub_statements logic so that when originid==SELF it is replaced by self.id() * Basic op checking for mock MainPOD * More op checking * wip * feat: add great boy example * feat: put examples under cfg(test) --------- Co-authored-by: Ahmad <root@ahmadafuni.com> Co-authored-by: arnaucube <git@arnaucube.com>
This commit is contained in:
parent
90e9782e62
commit
f2575d1524
5 changed files with 283 additions and 68 deletions
|
|
@ -99,30 +99,40 @@ statement loan_check(receiver: string):
|
||||||
A Good Boy Pod exposes one custom statement with one custom deduction rule.
|
A Good Boy Pod exposes one custom statement with one custom deduction rule.
|
||||||
|
|
||||||
```
|
```
|
||||||
statement good_boy(receiver: PubKey, good_boy_issuers: MerkleTree):
|
statement is_good_boy(user: PubKey, good_boy_issuers: MerkleTree):
|
||||||
- OR():
|
- OR():
|
||||||
- AND(pod: Pod):
|
- AND(pod: Pod, age: Int):
|
||||||
# A good boy issuer is my friend
|
|
||||||
- eq(pod.type, SIGNATURE)
|
- eq(pod.type, SIGNATURE)
|
||||||
- contains(good_boy_issuers, pod.signer)
|
- contains(good_boy_issuers, pod.signer)
|
||||||
- eq(pod.friend, receiver)
|
# A good boy issuer says this user is a good boy
|
||||||
|
- eq(pod.user, user)
|
||||||
|
- eq(pod.age, age)
|
||||||
|
```
|
||||||
|
|
||||||
|
A Friend Pod exposes one custom statement with one custom deduction rule.
|
||||||
|
|
||||||
|
```
|
||||||
|
statement is_friend(good_boy: PubKey, friend: PubKey, good_boy_issuers: MerkleTree):
|
||||||
|
- OR():
|
||||||
|
- AND(friend_pod: Pod):
|
||||||
|
- eq(pod.type, SIGNATURE)
|
||||||
|
# The issuer is a good boy
|
||||||
|
- is_good_boy(good_boy, good_boy_issuers)
|
||||||
|
# A good boy says this is their friend
|
||||||
|
- eq(pod.signer, good_boy)
|
||||||
|
- eq(pod.friend, friend)
|
||||||
```
|
```
|
||||||
|
|
||||||
A Great Boy Pod exposes (in addition to the above) one new custom statement
|
A Great Boy Pod exposes (in addition to the above) one new custom statement
|
||||||
with one custom deduction rule.
|
with one custom deduction rule.
|
||||||
|
|
||||||
```
|
```
|
||||||
statement great_boy(receiver: PubKey, good_boy_issuers: MerkleTree):
|
statement is_great_boy(great_boy: PubKey, good_boy_issuers: MerkleTree):
|
||||||
- OR():
|
- OR():
|
||||||
- AND(friend_pod_0: Pod, friend_pod_1: Pod):
|
- AND(friend_pod_0: Pod, friend_pod_1: Pod):
|
||||||
# good boy 0 is my friend
|
# Two good boys consider this user their friend
|
||||||
- eq(friend_pod_0.type, SIGNATURE)
|
- is_friend(friend_pod_0.signer, great_boy)
|
||||||
- good_boy(friend_pod_0.signer, good_boy_issuers)
|
- is_friend(friend_pod_1.signer, great_boy)
|
||||||
- eq(friend_pod_0.friend, receiver)
|
|
||||||
# good boy 1 is my friend
|
|
||||||
- eq(friend_pod_1.type, SIGNATURE)
|
|
||||||
- good_boy(friend_pod_1.signer, good_boy_issuers)
|
|
||||||
- eq(friend_pod_1.friend, receiver)
|
|
||||||
# good boy 0 != good boy 1
|
# good boy 0 != good boy 1
|
||||||
- neq(friend_pod_0.signer, friend_pod_1.signer)
|
- neq(friend_pod_0.signer, friend_pod_1.signer)
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -486,14 +486,14 @@ impl MainPod for MockMainPod {
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::backends::mock_signed::MockSigner;
|
use crate::backends::mock_signed::MockSigner;
|
||||||
use crate::frontend;
|
use crate::examples::{great_boy_pod_full_flow, zu_kyc_pod_builder, zu_kyc_sign_pod_builders};
|
||||||
use crate::middleware;
|
use crate::middleware;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mock_main_0() {
|
fn test_mock_main_zu_kyc() {
|
||||||
let params = middleware::Params::default();
|
let params = middleware::Params::default();
|
||||||
|
|
||||||
let (gov_id_builder, pay_stub_builder) = frontend::tests::zu_kyc_sign_pod_builders(¶ms);
|
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(¶ms);
|
||||||
let mut signer = MockSigner {
|
let mut signer = MockSigner {
|
||||||
pk: "ZooGov".into(),
|
pk: "ZooGov".into(),
|
||||||
};
|
};
|
||||||
|
|
@ -502,7 +502,7 @@ pub mod tests {
|
||||||
pk: "ZooDeel".into(),
|
pk: "ZooDeel".into(),
|
||||||
};
|
};
|
||||||
let pay_stub_pod = pay_stub_builder.sign(&mut signer).unwrap();
|
let pay_stub_pod = pay_stub_builder.sign(&mut signer).unwrap();
|
||||||
let kyc_builder = frontend::tests::zu_kyc_pod_builder(¶ms, &gov_id_pod, &pay_stub_pod);
|
let kyc_builder = zu_kyc_pod_builder(¶ms, &gov_id_pod, &pay_stub_pod);
|
||||||
|
|
||||||
let mut prover = MockProver {};
|
let mut prover = MockProver {};
|
||||||
let kyc_pod = kyc_builder.prove(&mut prover).unwrap();
|
let kyc_pod = kyc_builder.prove(&mut prover).unwrap();
|
||||||
|
|
@ -514,4 +514,21 @@ pub mod tests {
|
||||||
// println!("id: {}", pod.id());
|
// println!("id: {}", pod.id());
|
||||||
// println!("pub_statements: {:?}", pod.pub_statements());
|
// println!("pub_statements: {:?}", pod.pub_statements());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mock_main_great_boy() {
|
||||||
|
let great_boy_builder = great_boy_pod_full_flow();
|
||||||
|
|
||||||
|
let mut prover = MockProver {};
|
||||||
|
let great_boy_pod = great_boy_builder.prove(&mut prover).unwrap();
|
||||||
|
let pod = great_boy_pod
|
||||||
|
.pod
|
||||||
|
.into_any()
|
||||||
|
.downcast::<MockMainPod>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("{}", pod);
|
||||||
|
|
||||||
|
assert_eq!(pod.verify(), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
194
src/examples.rs
Normal file
194
src/examples.rs
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
use crate::frontend::{MainPodBuilder, MerkleTree, SignedPod, SignedPodBuilder, Value};
|
||||||
|
use crate::middleware::{Params, PodType, KEY_SIGNER, KEY_TYPE};
|
||||||
|
use crate::op;
|
||||||
|
|
||||||
|
// ZuKYC
|
||||||
|
|
||||||
|
pub fn zu_kyc_sign_pod_builders(params: &Params) -> (SignedPodBuilder, SignedPodBuilder) {
|
||||||
|
let mut gov_id = SignedPodBuilder::new(params);
|
||||||
|
gov_id.insert("idNumber", "4242424242");
|
||||||
|
gov_id.insert("dateOfBirth", 1169909384);
|
||||||
|
gov_id.insert("socialSecurityNumber", "G2121210");
|
||||||
|
|
||||||
|
let mut pay_stub = SignedPodBuilder::new(params);
|
||||||
|
pay_stub.insert("socialSecurityNumber", "G2121210");
|
||||||
|
pay_stub.insert("startDate", 1706367566);
|
||||||
|
|
||||||
|
(gov_id, pay_stub)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn zu_kyc_pod_builder(
|
||||||
|
params: &Params,
|
||||||
|
gov_id: &SignedPod,
|
||||||
|
pay_stub: &SignedPod,
|
||||||
|
) -> MainPodBuilder {
|
||||||
|
let sanction_list = Value::MerkleTree(MerkleTree { root: 1 });
|
||||||
|
let now_minus_18y: i64 = 1169909388;
|
||||||
|
let now_minus_1y: i64 = 1706367566;
|
||||||
|
|
||||||
|
let mut kyc = MainPodBuilder::new(params);
|
||||||
|
kyc.add_signed_pod(&gov_id);
|
||||||
|
kyc.add_signed_pod(&pay_stub);
|
||||||
|
kyc.pub_op(op!(not_contains, &sanction_list, (gov_id, "idNumber")));
|
||||||
|
kyc.pub_op(op!(lt, (gov_id, "dateOfBirth"), now_minus_18y));
|
||||||
|
kyc.pub_op(op!(
|
||||||
|
eq,
|
||||||
|
(gov_id, "socialSecurityNumber"),
|
||||||
|
(pay_stub, "socialSecurityNumber")
|
||||||
|
));
|
||||||
|
kyc.pub_op(op!(eq, (pay_stub, "startDate"), now_minus_1y));
|
||||||
|
|
||||||
|
kyc
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreatBoy
|
||||||
|
|
||||||
|
pub fn good_boy_sign_pod_builder(params: &Params, user: &str, age: i64) -> SignedPodBuilder {
|
||||||
|
let mut good_boy = SignedPodBuilder::new(params);
|
||||||
|
good_boy.insert("user", user);
|
||||||
|
good_boy.insert("age", age);
|
||||||
|
|
||||||
|
good_boy
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn friend_sign_pod_builder(params: &Params, friend: &str) -> SignedPodBuilder {
|
||||||
|
let mut friend_pod = SignedPodBuilder::new(params);
|
||||||
|
friend_pod.insert("friend", friend);
|
||||||
|
|
||||||
|
friend_pod
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn great_boy_pod_builder(
|
||||||
|
params: &Params,
|
||||||
|
good_boy_pods: [&SignedPod; 4],
|
||||||
|
friend_pods: [&SignedPod; 2],
|
||||||
|
good_boy_issuers: &Value,
|
||||||
|
receiver: &str,
|
||||||
|
) -> MainPodBuilder {
|
||||||
|
// Attestment chain (issuer -> good boy -> great boy):
|
||||||
|
// issuer 0 -> good_boy_pods[0] => good boy 0
|
||||||
|
// issuer 1 -> good_boy_pods[1] => good boy 0
|
||||||
|
// issuer 2 -> good_boy_pods[2] => good boy 1
|
||||||
|
// issuer 3 -> good_boy_pods[3] => good boy 1
|
||||||
|
// good boy 0 -> friend_pods[0] => receiver
|
||||||
|
// good boy 1 -> friend_pods[1] => receiver
|
||||||
|
|
||||||
|
let mut great_boy = MainPodBuilder::new(params);
|
||||||
|
for i in 0..4 {
|
||||||
|
great_boy.add_signed_pod(&good_boy_pods[i]);
|
||||||
|
}
|
||||||
|
for i in 0..2 {
|
||||||
|
great_boy.add_signed_pod(&friend_pods[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for good_boy_idx in 0..2 {
|
||||||
|
// Type check
|
||||||
|
great_boy.pub_op(op!(
|
||||||
|
eq,
|
||||||
|
(friend_pods[good_boy_idx], KEY_TYPE),
|
||||||
|
PodType::MockSigned as i64
|
||||||
|
));
|
||||||
|
for issuer_idx in 0..2 {
|
||||||
|
// Type check
|
||||||
|
great_boy.pub_op(op!(
|
||||||
|
eq,
|
||||||
|
(good_boy_pods[good_boy_idx * 2 + issuer_idx], KEY_TYPE),
|
||||||
|
PodType::MockSigned as i64
|
||||||
|
));
|
||||||
|
// Each good boy POD comes from a valid issuer
|
||||||
|
great_boy.pub_op(op!(
|
||||||
|
contains,
|
||||||
|
good_boy_issuers,
|
||||||
|
(good_boy_pods[good_boy_idx * 2 + issuer_idx], KEY_SIGNER)
|
||||||
|
));
|
||||||
|
// Each good boy has 2 good boy pods
|
||||||
|
great_boy.pub_op(op!(
|
||||||
|
eq,
|
||||||
|
(good_boy_pods[good_boy_idx * 2 + issuer_idx], "user"),
|
||||||
|
(friend_pods[good_boy_idx], KEY_SIGNER)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// The good boy PODs from each good boy have different issuers
|
||||||
|
great_boy.pub_op(op!(
|
||||||
|
ne,
|
||||||
|
(good_boy_pods[good_boy_idx * 2 + 0], KEY_SIGNER),
|
||||||
|
(good_boy_pods[good_boy_idx * 2 + 1], KEY_SIGNER)
|
||||||
|
));
|
||||||
|
// Each good boy is receivers' friend
|
||||||
|
great_boy.pub_op(op!(eq, (friend_pods[good_boy_idx], "friend"), receiver));
|
||||||
|
}
|
||||||
|
// The two good boys are different
|
||||||
|
great_boy.pub_op(op!(
|
||||||
|
ne,
|
||||||
|
(friend_pods[0], KEY_SIGNER),
|
||||||
|
(friend_pods[1], KEY_SIGNER)
|
||||||
|
));
|
||||||
|
|
||||||
|
great_boy
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn great_boy_pod_full_flow() -> MainPodBuilder {
|
||||||
|
use crate::backends::mock_signed::MockSigner;
|
||||||
|
|
||||||
|
let params = Params {
|
||||||
|
max_input_signed_pods: 6,
|
||||||
|
max_statements: 100,
|
||||||
|
max_public_statements: 50,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let good_boy_issuers = ["Giggles", "Macrosoft", "FaeBook"];
|
||||||
|
let mut giggles_signer = MockSigner {
|
||||||
|
pk: good_boy_issuers[0].into(),
|
||||||
|
};
|
||||||
|
let mut macrosoft_signer = MockSigner {
|
||||||
|
pk: good_boy_issuers[1].into(),
|
||||||
|
};
|
||||||
|
let mut faebook_signer = MockSigner {
|
||||||
|
pk: good_boy_issuers[2].into(),
|
||||||
|
};
|
||||||
|
let bob = "Bob";
|
||||||
|
let charlie = "Charlie";
|
||||||
|
let alice = "Alice";
|
||||||
|
let mut bob_signer = MockSigner { pk: bob.into() };
|
||||||
|
let mut charlie_signer = MockSigner { pk: charlie.into() };
|
||||||
|
|
||||||
|
// Bob receives two good_boy pods from Giggles and Macrosoft.
|
||||||
|
|
||||||
|
let bob = "Bob";
|
||||||
|
let mut bob_good_boys = Vec::new();
|
||||||
|
|
||||||
|
let good_boy = good_boy_sign_pod_builder(¶ms, &bob, 36);
|
||||||
|
bob_good_boys.push(good_boy.sign(&mut giggles_signer).unwrap());
|
||||||
|
bob_good_boys.push(good_boy.sign(&mut macrosoft_signer).unwrap());
|
||||||
|
|
||||||
|
// Charlie receives two good_boy pods from Macrosoft and Faebook
|
||||||
|
|
||||||
|
let charlie = "Charlie";
|
||||||
|
let mut charlie_good_boys = Vec::new();
|
||||||
|
|
||||||
|
let good_boy = good_boy_sign_pod_builder(¶ms, &charlie, 27);
|
||||||
|
charlie_good_boys.push(good_boy.sign(&mut macrosoft_signer).unwrap());
|
||||||
|
charlie_good_boys.push(good_boy.sign(&mut faebook_signer).unwrap());
|
||||||
|
|
||||||
|
// Bob and Charlie send Alice a Friend POD
|
||||||
|
|
||||||
|
let mut alice_friend_pods = Vec::new();
|
||||||
|
let friend = friend_sign_pod_builder(¶ms, &alice);
|
||||||
|
alice_friend_pods.push(friend.sign(&mut bob_signer).unwrap());
|
||||||
|
alice_friend_pods.push(friend.sign(&mut charlie_signer).unwrap());
|
||||||
|
|
||||||
|
let good_boy_issuers_mt = Value::MerkleTree(MerkleTree { root: 33 });
|
||||||
|
great_boy_pod_builder(
|
||||||
|
¶ms,
|
||||||
|
[
|
||||||
|
&bob_good_boys[0],
|
||||||
|
&bob_good_boys[1],
|
||||||
|
&charlie_good_boys[0],
|
||||||
|
&charlie_good_boys[1],
|
||||||
|
],
|
||||||
|
[&alice_friend_pods[0], &alice_friend_pods[1]],
|
||||||
|
&good_boy_issuers_mt,
|
||||||
|
alice,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -545,63 +545,44 @@ impl MainPodCompiler {
|
||||||
// TODO fn fmt_signed_pod_builder
|
// TODO fn fmt_signed_pod_builder
|
||||||
// TODO fn fmt_main_pod
|
// TODO fn fmt_main_pod
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
pub mod build_utils {
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! op_args {
|
||||||
|
($($arg:expr),+) => {vec![$(crate::frontend::OperationArg::from($arg)),*]}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! op {
|
||||||
|
(eq, $($arg:expr),+) => { crate::frontend::Operation(
|
||||||
|
crate::middleware::NativeOperation::EqualFromEntries,
|
||||||
|
crate::op_args!($($arg),*)) };
|
||||||
|
(ne, $($arg:expr),+) => { crate::frontend::Operation(
|
||||||
|
crate::middleware::NativeOperation::NotEqualFromEntries,
|
||||||
|
crate::op_args!($($arg),*)) };
|
||||||
|
(gt, $($arg:expr),+) => { crate::frontend::Operation(
|
||||||
|
crate::middleware::NativeOperation::GtFromEntries,
|
||||||
|
crate::op_args!($($arg),*)) };
|
||||||
|
(lt, $($arg:expr),+) => { crate::frontend::Operation(
|
||||||
|
crate::middleware::NativeOperation::LtFromEntries,
|
||||||
|
crate::op_args!($($arg),*)) };
|
||||||
|
(contains, $($arg:expr),+) => { crate::frontend::Operation(
|
||||||
|
crate::middleware::NativeOperation::ContainsFromEntries,
|
||||||
|
crate::op_args!($($arg),*)) };
|
||||||
|
(not_contains, $($arg:expr),+) => { crate::frontend::Operation(
|
||||||
|
crate::middleware::NativeOperation::NotContainsFromEntries,
|
||||||
|
crate::op_args!($($arg),*)) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::backends::mock_signed::MockSigner;
|
use crate::backends::mock_signed::MockSigner;
|
||||||
|
use crate::examples::{great_boy_pod_full_flow, zu_kyc_pod_builder, zu_kyc_sign_pod_builders};
|
||||||
macro_rules! args {
|
|
||||||
($($arg:expr),+) => {vec![$(OperationArg::from($arg)),*]}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! op {
|
|
||||||
(eq, $($arg:expr),+) => { Operation(NativeOperation::EqualFromEntries, args!($($arg),*)) };
|
|
||||||
(ne, $($arg:expr),+) => { Operation(NativeOperation::NotEqualFromEntries, args!($($arg),*)) };
|
|
||||||
(gt, $($arg:expr),+) => { Operation(NativeOperation::GtFromEntries, args!($($arg),*)) };
|
|
||||||
(lt, $($arg:expr),+) => { Operation(NativeOperation::LtFromEntries, args!($($arg),*)) };
|
|
||||||
(contains, $($arg:expr),+) => { Operation(NativeOperation::ContainsFromEntries, args!($($arg),*)) };
|
|
||||||
(not_contains, $($arg:expr),+) => { Operation(NativeOperation::NotContainsFromEntries, args!($($arg),*)) };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn zu_kyc_sign_pod_builders(params: &Params) -> (SignedPodBuilder, SignedPodBuilder) {
|
|
||||||
let mut gov_id = SignedPodBuilder::new(params);
|
|
||||||
gov_id.insert("idNumber", "4242424242");
|
|
||||||
gov_id.insert("dateOfBirth", 1169909384);
|
|
||||||
gov_id.insert("socialSecurityNumber", "G2121210");
|
|
||||||
|
|
||||||
let mut pay_stub = SignedPodBuilder::new(params);
|
|
||||||
pay_stub.insert("socialSecurityNumber", "G2121210");
|
|
||||||
pay_stub.insert("startDate", 1706367566);
|
|
||||||
|
|
||||||
(gov_id, pay_stub)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn zu_kyc_pod_builder(
|
|
||||||
params: &Params,
|
|
||||||
gov_id: &SignedPod,
|
|
||||||
pay_stub: &SignedPod,
|
|
||||||
) -> MainPodBuilder {
|
|
||||||
let sanction_list = Value::MerkleTree(MerkleTree { root: 1 });
|
|
||||||
let now_minus_18y: i64 = 1169909388;
|
|
||||||
let now_minus_1y: i64 = 1706367566;
|
|
||||||
|
|
||||||
let mut kyc = MainPodBuilder::new(¶ms);
|
|
||||||
kyc.add_signed_pod(&gov_id);
|
|
||||||
kyc.add_signed_pod(&pay_stub);
|
|
||||||
kyc.pub_op(op!(not_contains, &sanction_list, (gov_id, "idNumber")));
|
|
||||||
kyc.pub_op(op!(lt, (gov_id, "dateOfBirth"), now_minus_18y));
|
|
||||||
kyc.pub_op(op!(
|
|
||||||
eq,
|
|
||||||
(gov_id, "socialSecurityNumber"),
|
|
||||||
(pay_stub, "socialSecurityNumber")
|
|
||||||
));
|
|
||||||
kyc.pub_op(op!(eq, (pay_stub, "startDate"), now_minus_1y));
|
|
||||||
|
|
||||||
kyc
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_front_0() -> Result<()> {
|
fn test_front_zu_kyc() -> Result<()> {
|
||||||
let params = Params::default();
|
let params = Params::default();
|
||||||
let (gov_id, pay_stub) = zu_kyc_sign_pod_builders(¶ms);
|
let (gov_id, pay_stub) = zu_kyc_sign_pod_builders(¶ms);
|
||||||
|
|
||||||
|
|
@ -626,4 +607,14 @@ pub mod tests {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_front_great_boy() -> Result<()> {
|
||||||
|
let great_boy = great_boy_pod_full_flow();
|
||||||
|
println!("{}", great_boy);
|
||||||
|
|
||||||
|
// TODO: prove kyc with MockProver and print it
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,6 @@ pub mod backends;
|
||||||
pub mod frontend;
|
pub mod frontend;
|
||||||
pub mod merkletree;
|
pub mod merkletree;
|
||||||
pub mod middleware;
|
pub mod middleware;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod examples;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue