1use hmac::{Hmac, Mac};
2use sha2::Sha256;
3
4use hmac::crypto_mac::generic_array::typenum::*;
5use hmac::crypto_mac::generic_array::GenericArray;
6
7use std::marker::PhantomData;
8
9type HmacSha256 = Hmac<Sha256>;
11
12pub struct ProvablyFairConfig {
13 client_seed: String,
14 server_seed: String,
15 nonce: u64,
16}
17
18impl ProvablyFairConfig {
19 pub fn new(client_seed: &str, server_seed: &str, nonce: u64) -> ProvablyFairConfig {
20 ProvablyFairConfig {
21 client_seed: client_seed.to_string(),
22 server_seed: server_seed.to_string(),
23 nonce,
24 }
25 }
26}
27
28pub struct ProvablyFairRNG<T> {
29 config: ProvablyFairConfig,
30 current_round: u64,
31 current_round_cursor: usize,
32 current_round_mac: Option<GenericArray<u8, U32>>,
33 rng_type: PhantomData<T>,
34}
35
36impl<T> ProvablyFairRNG<T> {
65 pub fn from_config(config: ProvablyFairConfig) -> ProvablyFairRNG<T> {
66 ProvablyFairRNG {
67 config,
68 current_round: 0,
70 current_round_cursor: 0,
71 current_round_mac: None,
72 rng_type: PhantomData,
73 }
74 }
75
76 pub fn new(client_seed: &str, server_seed: &str, nonce: u64) -> ProvablyFairRNG<T> {
77 let config = ProvablyFairConfig {
78 client_seed: client_seed.to_string(),
79 server_seed: server_seed.to_string(),
80 nonce,
81 };
82 Self::from_config(config)
83 }
84
85 fn update_current_round_buffer(&mut self) {
86 let key = self.config.server_seed.as_bytes();
88 let input = format!(
89 "{}:{}:{}",
90 self.config.client_seed, self.config.nonce, self.current_round
91 );
92
93 let mut mac =
94 HmacSha256::new_varkey(key).expect("HMAC can take key of any size, never errors here");
95 mac.input(input.as_bytes());
96 let result = mac.result();
97 self.current_round_mac = Some(result.code());
98 }
99
100 fn next_byte(&mut self) -> u8 {
101 let mac = match &self.current_round_mac {
104 None => {
105 self.update_current_round_buffer();
106 return self.next_byte();
107 }
108 Some(v) => v,
109 };
110
111 let buf = mac;
112 let result = buf[self.current_round_cursor];
113 if self.current_round_cursor == 31 {
114 self.current_round_cursor = 0;
115 self.current_round += 1;
116 self.current_round_mac = None;
117 } else {
118 self.current_round_cursor += 1;
119 }
120 return result;
121 }
122
123 fn next_float(&mut self) -> f64 {
124 let bytes_per_float = 4;
125 let bytes = &mut [0; 4];
126 for i in 0..bytes_per_float {
127 let byte = self.next_byte();
128 bytes[i] = byte;
129 }
130 let result = bytes_to_float(bytes);
131 return result;
132 }
133}
134
135impl std::iter::Iterator for ProvablyFairRNG<f64> {
136 type Item = f64;
137 fn next(&mut self) -> Option<f64> {
138 Some(self.next_float())
139 }
140}
141
142impl std::iter::Iterator for ProvablyFairRNG<u8> {
143 type Item = u8;
144 fn next(&mut self) -> Option<u8> {
145 Some(self.next_byte())
146 }
147}
148
149fn bytes_to_float(bytes: &[u8]) -> f64 {
151 let (float, _) = bytes.iter().fold((0., 0.), |(result, i), &value| {
152 let value = value as f64;
153 let divider = 256_f64.powf(i + 1.);
154 let partial_result = value / divider as f64;
155 (result + partial_result, i + 1.)
156 });
157 float
158}
159
160impl ProvablyFairRNG<f64> {
162 pub fn range(&mut self, start: usize, end: usize) -> usize {
164 assert!(end > start);
165 let range = (end as i32 - start as i32) as usize;
166 (self.next().unwrap() * range as f64) as usize + start as usize
167 }
168}
169
170#[cfg(test)]
171mod test {
172 use super::*;
173
174 #[test]
175 fn provably_fair_rng() {
176 let client_seed = "some client seed";
177 let server_seed = "some server seed";
178 let nonce = 1;
179 let mut rng: ProvablyFairRNG<u8> = ProvablyFairRNG::new(client_seed, server_seed, nonce);
180 let expected_values = vec![
181 151, 136, 121, 135, 209, 159, 189, 233, 43, 248, 146, 253, 71, 34, 215, 176, 139, 160,
182 47, 225, 233, 221, 169, 198, 187, 103, 171, 31, 87, 118, 23, 138, 198, 14, 60, 130,
183 130, 198, 153, 83,
184 ];
185 for val in expected_values {
186 assert_eq!(rng.next(), Some(val));
187 }
188 }
189
190 #[test]
191 fn provably_fair_rng_float() {
192 let client_seed = "some client seed";
193 let server_seed = "some server seed";
194 let nonce = 1;
195 let mut rng: ProvablyFairRNG<f64> = ProvablyFairRNG::new(client_seed, server_seed, nonce);
196 let expected_values = vec![
197 0.5919261889066547,
198 0.81884371698834,
199 0.17176169087179005,
200 0.277875404804945,
201 0.5454130100551993,
202 0.913538561668247,
203 0.732050604885444,
204 0.34164569014683366,
205 0.7736547295935452,
206 0.5108428790699691,
207 ];
208 for val in expected_values {
209 let actual = rng.next();
210 assert_eq!(actual, Some(val));
212 }
213 }
214
215 #[test]
216 fn test_rng_float_starts_with_0() {
217 let client_seed = "83e27f682128eb1852b048203dfd6931";
218 let server_seed = "e8df2cc3b9ccb583ce5ea92336842387";
219 let nonce = 1942124;
220 let mut rng: ProvablyFairRNG<f64> = ProvablyFairRNG::new(client_seed, server_seed, nonce);
221 assert_eq!(rng.next().unwrap(), 0.00000025122426450252533);
222 }
223}