Skip to content

Commit

Permalink
Add LPN and GGM related (#54)
Browse files Browse the repository at this point in the history
* add lpn and optimizations with multi threads

* update comments

* finish ggm reconstruction, still need optimization

* clippy

* cargo ciplly

* add comments on public functions

* add pado as a contributor

* add assertion and modify documentation

* fix minor bugs

* rebase dev

* fmt
  • Loading branch information
xiangxiecrypto authored Oct 13, 2023
1 parent cd95659 commit 4736ae0
Show file tree
Hide file tree
Showing 9 changed files with 409 additions and 47 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
## Contributors

- [TLSNotary](https://github.com/tlsnotary)
- [PADO Labs](https://github.com/pado-labs)


### Pronounciation
Expand Down
9 changes: 8 additions & 1 deletion mpz-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ edition = "2021"
name = "mpz_core"

[features]
default = ["cointoss"]
default = ["cointoss", "rayon"]
cointoss = ["dep:rand_chacha"]
rayon = ["dep:rayon"]

[dependencies]
aes = { workspace = true, features = [] }
Expand All @@ -26,6 +27,8 @@ bcs = "0.1.5"
rand_core = "0.6.4"
bytemuck = { workspace = true, features = ["derive"] }
generic-array.workspace = true
rayon = { workspace = true, optional = true }
cfg-if.workspace = true

[dev-dependencies]
rstest.workspace = true
Expand All @@ -42,3 +45,7 @@ harness = false
[[bench]]
name = "prg"
harness = false

[[bench]]
name = "lpn"
harness = false
19 changes: 15 additions & 4 deletions mpz-core/benches/ggm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ use mpz_core::{block::Block, ggm_tree::GgmTree};
#[allow(clippy::all)]
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("ggm::gen::1K", move |bench| {
let depth = 11;
let depth = 10;
let ggm = GgmTree::new(depth);
let mut tree = vec![Block::ZERO; 1 << (depth - 1)];
let mut k0 = vec![Block::ZERO; depth - 1];
let mut k1 = vec![Block::ZERO; depth - 1];
let mut tree = vec![Block::ZERO; 1 << (depth)];
let mut k0 = vec![Block::ZERO; depth];
let mut k1 = vec![Block::ZERO; depth];
let seed = rand::random::<Block>();
bench.iter(|| {
black_box(ggm.gen(
Expand All @@ -19,6 +19,17 @@ fn criterion_benchmark(c: &mut Criterion) {
));
});
});

c.bench_function("ggm::reconstruction::1K", move |bench| {
let depth = 10;
let ggm = GgmTree::new(depth);
let mut tree = vec![Block::ZERO; 1 << (depth)];
let k = vec![Block::ZERO; depth];
let alpha = vec![false; depth];
bench.iter(|| {
black_box(ggm.reconstruct(black_box(&mut tree), black_box(&k), black_box(&alpha)))
});
});
}

criterion_group!(benches, criterion_benchmark);
Expand Down
57 changes: 57 additions & 0 deletions mpz-core/benches/lpn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use mpz_core::{lpn::LpnEncoder, prg::Prg, Block};
use std::time::Duration;

fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("lpn-rayon-small", move |bench| {
let seed = Block::ZERO;
let k = 5_060;
let n = 166_400;
let lpn = LpnEncoder::<10>::new(seed, k);
let mut x = vec![Block::ZERO; k as usize];
let mut y = vec![Block::ZERO; n];
let mut prg = Prg::new();
prg.random_blocks(&mut x);
prg.random_blocks(&mut y);
bench.iter(|| {
black_box(lpn.compute(&mut y, &x));
});
});

c.bench_function("lpn-rayon-medium", move |bench| {
let seed = Block::ZERO;
let k = 158_000;
let n = 10_168_320;
let lpn = LpnEncoder::<10>::new(seed, k);
let mut x = vec![Block::ZERO; k as usize];
let mut y = vec![Block::ZERO; n];
let mut prg = Prg::new();
prg.random_blocks(&mut x);
prg.random_blocks(&mut y);
bench.iter(|| {
black_box(lpn.compute(&mut y, &x));
});
});

c.bench_function("lpn-rayon-large", move |bench| {
let seed = Block::ZERO;
let k = 588_160;
let n = 10_616_092;
let lpn = LpnEncoder::<10>::new(seed, k);
let mut x = vec![Block::ZERO; k as usize];
let mut y = vec![Block::ZERO; n];
let mut prg = Prg::new();
prg.random_blocks(&mut x);
prg.random_blocks(&mut y);
bench.iter(|| {
black_box(lpn.compute(&mut y, &x));
});
});
}

criterion_group! {
name = lpn;
config = Criterion::default().warm_up_time(Duration::from_millis(1000)).sample_size(10);
targets = criterion_benchmark
}
criterion_main!(lpn);
149 changes: 110 additions & 39 deletions mpz-core/src/ggm_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,20 @@ impl GgmTree {
Self { tkprp, depth }
}

/// Input: `seed`: a seed.
/// Output: `tree`: a GGM (binary tree) `tree`, with size `2^{depth-1}`
/// Output: `k0`: XORs of all the left-node values in each level, with size `depth-1`.
/// Output: `k1`: XORs of all the right-node values in each level, with size `depth-1`.
/// This implementation is adapted from EMP Toolkit.
/// Create a GGM tree in-place.
///
/// # Arguments
///
/// * `seed` - a seed.
/// * `tree` - the destination to write the GGM (binary tree) `tree`, with size `2^{depth}`.
/// * `k0` - XORs of all the left-node values in each level, with size `depth`.
/// * `k1`- XORs of all the right-node values in each level, with size `depth`.
// This implementation is adapted from EMP Toolkit.
pub fn gen(&self, seed: Block, tree: &mut [Block], k0: &mut [Block], k1: &mut [Block]) {
assert!(tree.len() == 1 << (self.depth - 1));
assert!(k0.len() == self.depth - 1);
assert!(k1.len() == self.depth - 1);
let mut buf = vec![Block::ZERO; 8];
assert_eq!(tree.len(), 1 << (self.depth));
assert_eq!(k0.len(), self.depth);
assert_eq!(k1.len(), self.depth);
let mut buf = [Block::ZERO; 8];
self.tkprp.expand_1to2(tree, seed);
k0[0] = tree[0];
k1[0] = tree[1];
Expand All @@ -37,9 +41,11 @@ impl GgmTree {
k1[1] = buf[1] ^ buf[3];
tree[0..4].copy_from_slice(&buf[0..4]);

for h in 2..self.depth - 1 {
for h in 2..self.depth {
k0[h] = Block::ZERO;
k1[h] = Block::ZERO;

// How many nodes there are in this layer
let sz = 1 << h;
for i in (0..=sz - 4).rev().step_by(4) {
self.tkprp.expand_4to8(&mut buf, &tree[i..]);
Expand All @@ -56,43 +62,108 @@ impl GgmTree {
}
}
}

/// Reconstruct the GGM tree except the value in a given position.
///
/// This reconstructs the GGM tree entirely except `tree[pos] == Block::ZERO`. The bit decomposition of `pos` is the complement of `alpha`. i.e., `pos[i] = 1 xor alpha[i]`.
///
/// # Arguments
///
/// * `k` - a slice of blocks with length `depth`, the values of k are chosen via OT from k0 and k1. For the i-th value, if alpha[i] == 1, k[i] = k1[i]; else k[i] = k0[i].
/// * `alpha` - a slice of bits with length `depth`.
/// * `tree` - the destination to write the GGM tree.
pub fn reconstruct(&self, tree: &mut [Block], k: &[Block], alpha: &[bool]) {
assert_eq!(tree.len(), 1 << (self.depth));
assert_eq!(k.len(), self.depth);
assert_eq!(alpha.len(), self.depth);

let mut pos = 0;
for i in 1..=self.depth {
pos *= 2;
tree[pos] = Block::ZERO;
tree[pos + 1] = Block::ZERO;
if !alpha[i - 1] {
self.reconstruct_layer(i, false, pos, k[i - 1], tree);
pos += 1;
} else {
self.reconstruct_layer(i, true, pos + 1, k[i - 1], tree);
}
}
}

// Handle each layer.
fn reconstruct_layer(
&self,
depth: usize,
left_or_right: bool,
pos: usize,
k: Block,
tree: &mut [Block],
) {
// How many nodes there are in this layer
let sz = 1 << depth;

let mut sum = Block::ZERO;
let start = if left_or_right { 1 } else { 0 };

for i in (start..sz).step_by(2) {
sum ^= tree[i];
}
tree[pos] = sum ^ k;

if depth == (self.depth) {
return;
}

let mut buf = [Block::ZERO; 8];
if sz == 2 {
self.tkprp.expand_2to4(&mut buf, tree);
tree[0..4].copy_from_slice(&buf[0..4]);
} else {
for i in (0..=sz - 4).rev().step_by(4) {
self.tkprp.expand_4to8(&mut buf, &tree[i..]);
tree[2 * i..2 * i + 8].copy_from_slice(&buf);
}
}
}
}

#[test]
fn ggm_test() {
use crate::ggm_tree::GgmTree;
use crate::Block;

let depth = 3;
let mut tree = vec![Block::ZERO; 1 << (depth - 1)];
let mut k0 = vec![Block::ZERO; depth - 1];
let mut k1 = vec![Block::ZERO; depth - 1];
let mut tree = vec![Block::ZERO; 1 << depth];
let mut k0 = vec![Block::ZERO; depth];
let mut k1 = vec![Block::ZERO; depth];
let mut k = vec![Block::ZERO; depth];
let alpha = [false, true, false];
let mut pos = 0;

for a in alpha {
pos <<= 1;
if !a {
pos += 1;
}
}

let ggm = GgmTree::new(depth);

ggm.gen(Block::ZERO, &mut tree, &mut k0, &mut k1);

// Test vectors are from EMP Toolkit.
assert_eq!(
tree,
[
Block::from(0x92A6DDEAA3E99F9BECB268BD9EF67C91_u128.to_le_bytes()),
Block::from(0x9E7E9C02ED1E62385EE8A9EDDC63A2B5_u128.to_le_bytes()),
Block::from(0xBD4B85E90AACBD106694537DB6251264_u128.to_le_bytes()),
Block::from(0x230485DC4360014833E07D8D914411A2_u128.to_le_bytes()),
]
);

assert_eq!(
k0,
[
Block::from(0x2E2B34CA59FA4C883B2C8AEFD44BE966_u128.to_le_bytes()),
Block::from(0x2FED5803A945228B8A263BC028D36EF5_u128.to_le_bytes()),
]
);

assert_eq!(
k1,
[
Block::from(0x7E46C568D1CD4972BB1A61F95DD80EDC_u128.to_le_bytes()),
Block::from(0xBD7A19DEAE7E63706D08D4604D27B317_u128.to_le_bytes()),
]
);
for i in 0..depth {
if alpha[i] {
k[i] = k1[i];
} else {
k[i] = k0[i];
}
}

let mut tree_reconstruct = vec![Block::ZERO; 1 << depth];
ggm.reconstruct(&mut tree_reconstruct, &k, &alpha);

assert_eq!(tree_reconstruct[pos], Block::ZERO);
tree_reconstruct[pos] = tree[pos];
assert_eq!(tree, tree_reconstruct);
}
2 changes: 2 additions & 0 deletions mpz-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ pub mod cointoss;
pub mod commit;
pub mod ggm_tree;
pub mod hash;
pub mod lpn;
pub mod prg;
pub mod prp;
pub mod serialize;
pub mod tkprp;
pub mod utils;
Expand Down
Loading

0 comments on commit 4736ae0

Please sign in to comment.