1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use blake2s_simd::blake2s;
use byteorder::{LittleEndian, WriteBytesExt};
use num_bigint::BigUint;
use num_traits::cast::ToPrimitive;
use crate::hasher::Domain;
use crate::layered_drgporep::LayerChallenges;
pub fn derive_challenges<D: Domain>(
challenges: &LayerChallenges,
layer: u8,
leaves: usize,
replica_id: &D,
commitment: &D,
k: u8,
) -> Vec<usize> {
let n = challenges.challenges_for_layer(layer as usize);
(0..n)
.map(|i| {
let mut bytes = replica_id.into_bytes();
let j = ((n * k as usize) + i) as u32;
bytes.extend(commitment.into_bytes());
bytes.push(layer);
bytes.write_u32::<LittleEndian>(j).unwrap();
let hash = blake2s(bytes.as_slice());
let big_challenge = BigUint::from_bytes_le(hash.as_ref());
let big_mod_challenge = big_challenge % (leaves - 2);
let big_mod_challenge = big_mod_challenge
.to_usize()
.expect("`big_mod_challenge` exceeds size of `usize`");
big_mod_challenge + 1
})
.collect()
}
#[cfg(test)]
mod test {
use super::*;
use crate::hasher::pedersen::PedersenDomain;
use rand::{thread_rng, Rng};
use std::collections::HashMap;
#[test]
fn challenge_derivation() {
let n = 200;
let layers = 100;
let challenges = LayerChallenges::new_fixed(layers, n);
let leaves = 1 << 30;
let mut rng = thread_rng();
let replica_id: PedersenDomain = rng.gen();
let commitment: PedersenDomain = rng.gen();
let partitions = 5;
let total_challenges = partitions * n;
let mut layers_with_duplicates = 0;
for layer in 0..layers {
let mut histogram = HashMap::new();
for k in 0..partitions {
let challenges = derive_challenges(
&challenges,
layer as u8,
leaves,
&replica_id,
&commitment,
k as u8,
);
for challenge in challenges {
let counter = histogram.entry(challenge).or_insert(0);
*counter += 1;
}
}
let unique_challenges = histogram.len();
if unique_challenges < total_challenges {
layers_with_duplicates += 1;
}
}
assert!(layers_with_duplicates < 3);
}
#[test]
fn challenge_partition_equivalence() {
let n = 40;
let leaves = 1 << 30;
let mut rng = thread_rng();
let replica_id: PedersenDomain = rng.gen();
let commitment: PedersenDomain = rng.gen();
let partitions = 5;
let layers = 100;
let total_challenges = n * partitions;
for layer in 0..layers {
let one_partition_challenges = derive_challenges(
&LayerChallenges::new_fixed(layers, total_challenges),
layer as u8,
leaves,
&replica_id,
&commitment,
0,
);
let many_partition_challenges = (0..partitions)
.flat_map(|k| {
derive_challenges(
&LayerChallenges::new_fixed(layers, n),
layer as u8,
leaves,
&replica_id,
&commitment,
k as u8,
)
})
.collect::<Vec<_>>();
assert_eq!(one_partition_challenges, many_partition_challenges);
}
}
}