soroban_sdk/prng.rs
1//! Prng contains a pseudo-random number generator.
2//!
3//! ## Warning
4//!
5//! Do not use the PRNG in this module without a clear understanding of two
6//! major limitations in the way it is deployed in the Stellar network:
7//!
8//! 1. The PRNG is seeded with data that is public as soon as each ledger is
9//! nominated. Therefore it **should never be used to generate secrets**.
10//!
11//! 2. The PRNG is seeded with data that is under the control of validators.
12//! Therefore it **should only be used in applications where the risk of
13//! validator influence is acceptable**.
14//!
15//! The PRNG in this module is a strong CSPRNG (ChaCha20) and can be manually
16//! re-seeded by contracts, in order to support commit/reveal schemes, oracles,
17//! or similar advanced types of pseudo-random contract behaviour. Any PRNG is
18//! however only as strong as its seed.
19//!
20//! The network runs in strict consensus, so every node in the network seeds its
21//! PRNG with a consensus value, **not a random entropy source**. It uses data
22//! that is generally difficult to predict in advance, and generally difficult
23//! for network **users** to bias to a specific value: the seed is derived from
24//! the overall transaction-set hash and the hash-sorted position number of each
25//! transaction within it. But this seed is **not secret** and **not
26//! cryptographically hard to bias** if a corrupt **validator** were to choose
27//! to do so (similar to the way a corrupt validator can bias overall
28//! transaction admission in the network).
29//!
30//! In other words the network will provide a stronger seed than a contract
31//! could likely derive on-chain using any other public data visible to it (eg.
32//! better than using a timestamp, ledger number, counter, or a similarly weak
33//! seed) but weaker than a contract could acquire using a commit/reveal scheme
34//! with an off-chain source of trusted random entropy.
35//!
36//! You should carefully consider whether these limitations are acceptable for
37//! your application before using this module.
38//!
39//! ## Operation
40//!
41//! The host has a single hidden "base" PRNG that is seeded by the network. The
42//! base PRNG is then used to seed separate, independent "local" PRNGs for each
43//! contract invocation. This independence has the following characteristics:
44//!
45//! - Contract invocations can only access (use or reseed) their local PRNG.
46//! - Contract invocations cannot influence any other invocation's local PRNG,
47//! except by influencing the other invocation to make a call to its PRNG.
48//! - Contracts cannot influence the base PRNG that seeds local PRNGs, except
49//! by making calls and thereby creating new local PRNGs with new seeds.
50//! - A contract invocation's local PRNG maintains state through the life of
51//! the invocation.
52//! - That state is advanced by each call from the invocation to a PRNG
53//! function in this module.
54//! - A contract invocation's local PRNG is destroyed after the invocation.
55//! - Any re-entry of a contract counts as a separate invocation.
56//!
57//! ## Testing
58//!
59//! In local tests, the base PRNG of each host is seeded to zero when the host
60//! is constructed, so each contract invocation's local PRNG seed (and all its
61//! PRNG-derived calls) will be determined strictly by its order of invocation
62//! in the test. Assuming this order is stable, each test run should see stable
63//! output from the local PRNG.
64use core::ops::{Bound, RangeBounds};
65
66use crate::{
67 env::internal,
68 unwrap::{UnwrapInfallible, UnwrapOptimized},
69 Bytes, BytesN, Env, IntoVal, Vec,
70};
71
72/// Prng is a pseudo-random generator.
73///
74/// # Warning
75///
76/// **The PRNG is unsuitable for generating secrets or use in applications with
77/// low risk tolerance, see the module-level comment.**
78pub struct Prng {
79 env: Env,
80}
81
82impl Prng {
83 pub(crate) fn new(env: &Env) -> Prng {
84 Prng { env: env.clone() }
85 }
86
87 pub fn env(&self) -> &Env {
88 &self.env
89 }
90
91 /// Reseeds the PRNG with the provided value.
92 ///
93 /// # Warning
94 ///
95 /// **The PRNG is unsuitable for generating secrets or use in applications with
96 /// low risk tolerance, see the module-level comment.**
97 pub fn seed(&self, seed: Bytes) {
98 let env = self.env();
99 assert_in_contract!(env);
100 internal::Env::prng_reseed(env, seed.into()).unwrap_infallible();
101 }
102
103 /// Fills the type with a random value.
104 ///
105 /// # Warning
106 ///
107 /// **The PRNG is unsuitable for generating secrets or use in applications with
108 /// low risk tolerance, see the module-level comment.**
109 ///
110 /// # Examples
111 ///
112 /// ## `u64`
113 ///
114 /// ```
115 /// # use soroban_sdk::{Env, contract, contractimpl, symbol_short, Bytes};
116 /// #
117 /// # #[contract]
118 /// # pub struct Contract;
119 /// #
120 /// # #[cfg(feature = "testutils")]
121 /// # fn main() {
122 /// # let env = Env::default();
123 /// # let contract_id = env.register(Contract, ());
124 /// # env.as_contract(&contract_id, || {
125 /// # env.prng().seed(Bytes::from_array(&env, &[1; 32]));
126 /// let mut value: u64 = 0;
127 /// env.prng().fill(&mut value);
128 /// assert_eq!(value, 8478755077819529274);
129 /// # })
130 /// # }
131 /// # #[cfg(not(feature = "testutils"))]
132 /// # fn main() { }
133 /// ```
134 ///
135 /// ## `[u8]`
136 ///
137 /// ```
138 /// # use soroban_sdk::{Env, contract, contractimpl, symbol_short, Bytes};
139 /// #
140 /// # #[contract]
141 /// # pub struct Contract;
142 /// #
143 /// # #[cfg(feature = "testutils")]
144 /// # fn main() {
145 /// # let env = Env::default();
146 /// # let contract_id = env.register(Contract, ());
147 /// # env.as_contract(&contract_id, || {
148 /// # env.prng().seed(Bytes::from_array(&env, &[1; 32]));
149 /// let mut value = [0u8; 32];
150 /// env.prng().fill(&mut value);
151 /// assert_eq!(
152 /// value,
153 /// [
154 /// 58, 248, 248, 38, 210, 150, 170, 117, 122, 110, 9, 101, 244, 57,
155 /// 221, 102, 164, 48, 43, 104, 222, 229, 242, 29, 25, 148, 88, 204,
156 /// 130, 148, 2, 66
157 /// ],
158 /// );
159 /// # })
160 /// # }
161 /// # #[cfg(not(feature = "testutils"))]
162 /// # fn main() { }
163 /// ```
164 pub fn fill<T>(&self, v: &mut T)
165 where
166 T: Fill + ?Sized,
167 {
168 v.fill(self);
169 }
170
171 /// Returns a random value of the given type.
172 ///
173 /// # Warning
174 ///
175 /// **The PRNG is unsuitable for generating secrets or use in applications with
176 /// low risk tolerance, see the module-level comment.**
177 ///
178 /// # Examples
179 ///
180 /// ## `u64`
181 ///
182 /// ```
183 /// # use soroban_sdk::{Env, contract, contractimpl, symbol_short, Bytes};
184 /// #
185 /// # #[contract]
186 /// # pub struct Contract;
187 /// #
188 /// # #[cfg(feature = "testutils")]
189 /// # fn main() {
190 /// # let env = Env::default();
191 /// # let contract_id = env.register(Contract, ());
192 /// # env.as_contract(&contract_id, || {
193 /// # env.prng().seed(Bytes::from_array(&env, &[1; 32]));
194 /// let value: u64 = env.prng().gen();
195 /// assert_eq!(value, 8478755077819529274);
196 /// # })
197 /// # }
198 /// # #[cfg(not(feature = "testutils"))]
199 /// # fn main() { }
200 /// ```
201 ///
202 /// ## `[u8; N]`
203 ///
204 /// ```
205 /// # use soroban_sdk::{Env, contract, contractimpl, symbol_short, Bytes};
206 /// #
207 /// # #[contract]
208 /// # pub struct Contract;
209 /// #
210 /// # #[cfg(feature = "testutils")]
211 /// # fn main() {
212 /// # let env = Env::default();
213 /// # let contract_id = env.register(Contract, ());
214 /// # env.as_contract(&contract_id, || {
215 /// # env.prng().seed(Bytes::from_array(&env, &[1; 32]));
216 /// let value: [u8; 32] = env.prng().gen();
217 /// assert_eq!(
218 /// value,
219 /// [
220 /// 58, 248, 248, 38, 210, 150, 170, 117, 122, 110, 9, 101, 244, 57,
221 /// 221, 102, 164, 48, 43, 104, 222, 229, 242, 29, 25, 148, 88, 204,
222 /// 130, 148, 2, 66
223 /// ],
224 /// );
225 /// # })
226 /// # }
227 /// # #[cfg(not(feature = "testutils"))]
228 /// # fn main() { }
229 /// ```
230 pub fn gen<T>(&self) -> T
231 where
232 T: Gen,
233 {
234 T::gen(self)
235 }
236
237 /// Returns a random value of the given type with the given length.
238 ///
239 /// # Panics
240 ///
241 /// If the length is greater than u32::MAX.
242 ///
243 /// # Warning
244 ///
245 /// **The PRNG is unsuitable for generating secrets or use in applications with
246 /// low risk tolerance, see the module-level comment.**
247 ///
248 /// # Examples
249 ///
250 /// ## `Bytes`
251 ///
252 /// ```
253 /// # use soroban_sdk::{Env, contract, contractimpl, symbol_short, Bytes};
254 /// #
255 /// # #[contract]
256 /// # pub struct Contract;
257 /// #
258 /// # #[cfg(feature = "testutils")]
259 /// # fn main() {
260 /// # let env = Env::default();
261 /// # let contract_id = env.register(Contract, ());
262 /// # env.as_contract(&contract_id, || {
263 /// # env.prng().seed(Bytes::from_array(&env, &[1; 32]));
264 /// // Get a value of length 32 bytes.
265 /// let value: Bytes = env.prng().gen_len(32);
266 /// assert_eq!(value, Bytes::from_slice(
267 /// &env,
268 /// &[
269 /// 58, 248, 248, 38, 210, 150, 170, 117, 122, 110, 9, 101, 244, 57,
270 /// 221, 102, 164, 48, 43, 104, 222, 229, 242, 29, 25, 148, 88, 204,
271 /// 130, 148, 2, 66
272 /// ],
273 /// ));
274 /// # })
275 /// # }
276 /// # #[cfg(not(feature = "testutils"))]
277 /// # fn main() { }
278 /// ```
279 pub fn gen_len<T>(&self, len: T::Len) -> T
280 where
281 T: GenLen,
282 {
283 T::gen_len(self, len)
284 }
285
286 /// Returns a random value of the given type in the range specified.
287 ///
288 /// # Panics
289 ///
290 /// If the start of the range is greater than the end.
291 ///
292 /// # Warning
293 ///
294 /// **The PRNG is unsuitable for generating secrets or use in applications with
295 /// low risk tolerance, see the module-level comment.**
296 ///
297 /// # Examples
298 ///
299 /// ## `u64`
300 ///
301 /// ```
302 /// # use soroban_sdk::{Env, contract, contractimpl, symbol_short, Bytes};
303 /// #
304 /// # #[contract]
305 /// # pub struct Contract;
306 /// #
307 /// # #[cfg(feature = "testutils")]
308 /// # fn main() {
309 /// # let env = Env::default();
310 /// # let contract_id = env.register(Contract, ());
311 /// # env.as_contract(&contract_id, || {
312 /// # env.prng().seed(Bytes::from_array(&env, &[1; 32]));
313 /// // Get a value in the range of 1 to 100, inclusive.
314 /// let value: u64 = env.prng().gen_range(1..=100);
315 /// assert_eq!(value, 46);
316 /// # })
317 /// # }
318 /// # #[cfg(not(feature = "testutils"))]
319 /// # fn main() { }
320 /// ```
321 pub fn gen_range<T>(&self, r: impl RangeBounds<T::RangeBound>) -> T
322 where
323 T: GenRange,
324 {
325 T::gen_range(self, r)
326 }
327
328 /// Returns a random u64 in the range specified.
329 ///
330 /// # Panics
331 ///
332 /// If the range is empty.
333 ///
334 /// # Warning
335 ///
336 /// **The PRNG is unsuitable for generating secrets or use in applications with
337 /// low risk tolerance, see the module-level comment.**
338 ///
339 /// # Examples
340 ///
341 /// ```
342 /// # use soroban_sdk::{Env, contract, contractimpl, symbol_short, Bytes};
343 /// #
344 /// # #[contract]
345 /// # pub struct Contract;
346 /// #
347 /// # #[cfg(feature = "testutils")]
348 /// # fn main() {
349 /// # let env = Env::default();
350 /// # let contract_id = env.register(Contract, ());
351 /// # env.as_contract(&contract_id, || {
352 /// # env.prng().seed(Bytes::from_array(&env, &[1; 32]));
353 /// // Get a value in the range of 1 to 100, inclusive.
354 /// let value = env.prng().u64_in_range(1..=100);
355 /// assert_eq!(value, 46);
356 /// # })
357 /// # }
358 /// # #[cfg(not(feature = "testutils"))]
359 /// # fn main() { }
360 /// ```
361 #[deprecated(note = "use env.prng().gen_range(...)")]
362 pub fn u64_in_range(&self, r: impl RangeBounds<u64>) -> u64 {
363 self.gen_range(r)
364 }
365
366 /// Shuffles a value using the Fisher-Yates algorithm.
367 ///
368 /// # Warning
369 ///
370 /// **The PRNG is unsuitable for generating secrets or use in applications with
371 /// low risk tolerance, see the module-level comment.**
372 pub fn shuffle<T>(&self, v: &mut T)
373 where
374 T: Shuffle,
375 {
376 v.shuffle(self);
377 }
378}
379
380impl<T> Shuffle for Vec<T> {
381 fn shuffle(&mut self, prng: &Prng) {
382 let env = prng.env();
383 assert_in_contract!(env);
384 let obj = internal::Env::prng_vec_shuffle(env, self.to_object()).unwrap_infallible();
385 *self = unsafe { Self::unchecked_new(env.clone(), obj) };
386 }
387}
388
389/// Implemented by types that support being filled by a Prng.
390pub trait Fill {
391 /// Fills the given value with the Prng.
392 fn fill(&mut self, prng: &Prng);
393}
394
395/// Implemented by types that support being generated by a Prng.
396pub trait Gen {
397 /// Generates a value of the implementing type with the Prng.
398 fn gen(prng: &Prng) -> Self;
399}
400
401/// Implemented by types that support being generated of specific length by a
402/// Prng.
403pub trait GenLen {
404 type Len;
405
406 /// Generates a value of the given implementing type with length with the
407 /// Prng.
408 ///
409 /// # Panics
410 ///
411 /// If the length is greater than u32::MAX.
412 fn gen_len(prng: &Prng, len: Self::Len) -> Self;
413}
414
415/// Implemented by types that support being generated in a specific range by a
416/// Prng.
417pub trait GenRange {
418 type RangeBound;
419
420 /// Generates a value of the implementing type with the Prng in the
421 /// specified range.
422 ///
423 /// # Panics
424 ///
425 /// If the range is empty.
426 fn gen_range(prng: &Prng, r: impl RangeBounds<Self::RangeBound>) -> Self;
427}
428
429/// Implemented by types that support being shuffled by a Prng.
430pub trait Shuffle {
431 /// Shuffles the value with the Prng.
432 fn shuffle(&mut self, prng: &Prng);
433}
434
435/// Implemented by types that support being shuffled by a Prng.
436pub trait ToShuffled {
437 type Shuffled;
438 fn to_shuffled(&self, prng: &Prng) -> Self::Shuffled;
439}
440
441impl<T: Shuffle + Clone> ToShuffled for T {
442 type Shuffled = Self;
443 fn to_shuffled(&self, prng: &Prng) -> Self {
444 let mut copy = self.clone();
445 copy.shuffle(prng);
446 copy
447 }
448}
449
450impl Fill for u64 {
451 fn fill(&mut self, prng: &Prng) {
452 *self = Self::gen(prng);
453 }
454}
455
456impl Gen for u64 {
457 fn gen(prng: &Prng) -> Self {
458 let env = prng.env();
459 assert_in_contract!(env);
460 internal::Env::prng_u64_in_inclusive_range(env, u64::MIN, u64::MAX).unwrap_infallible()
461 }
462}
463
464impl GenRange for u64 {
465 type RangeBound = u64;
466
467 fn gen_range(prng: &Prng, r: impl RangeBounds<Self::RangeBound>) -> Self {
468 let env = prng.env();
469 assert_in_contract!(env);
470 let start_bound = match r.start_bound() {
471 Bound::Included(b) => *b,
472 Bound::Excluded(b) => *b + 1,
473 Bound::Unbounded => u64::MIN,
474 };
475 let end_bound = match r.end_bound() {
476 Bound::Included(b) => *b,
477 Bound::Excluded(b) => *b - 1,
478 Bound::Unbounded => u64::MAX,
479 };
480 internal::Env::prng_u64_in_inclusive_range(env, start_bound, end_bound).unwrap_infallible()
481 }
482}
483
484impl Fill for Bytes {
485 /// Fills the Bytes with the Prng.
486 ///
487 /// # Panics
488 ///
489 /// If the length of Bytes is greater than u32::MAX in length.
490 fn fill(&mut self, prng: &Prng) {
491 let env = prng.env();
492 assert_in_contract!(env);
493 let len: u32 = self.len();
494 let obj = internal::Env::prng_bytes_new(env, len.into()).unwrap_infallible();
495 *self = unsafe { Bytes::unchecked_new(env.clone(), obj) };
496 }
497}
498
499impl GenLen for Bytes {
500 type Len = u32;
501 /// Generates the Bytes with the Prng of the given length.
502 fn gen_len(prng: &Prng, len: u32) -> Self {
503 let env = prng.env();
504 assert_in_contract!(env);
505 let obj = internal::Env::prng_bytes_new(env, len.into()).unwrap_infallible();
506 unsafe { Bytes::unchecked_new(env.clone(), obj) }
507 }
508}
509
510impl<const N: usize> Fill for BytesN<N> {
511 /// Fills the BytesN with the Prng.
512 ///
513 /// # Panics
514 ///
515 /// If the length of BytesN is greater than u32::MAX in length.
516 fn fill(&mut self, prng: &Prng) {
517 let bytesn = Self::gen(prng);
518 *self = bytesn;
519 }
520}
521
522impl<const N: usize> Gen for BytesN<N> {
523 /// Generates the BytesN with the Prng.
524 ///
525 /// # Panics
526 ///
527 /// If the length of BytesN is greater than u32::MAX in length.
528 fn gen(prng: &Prng) -> Self {
529 let env = prng.env();
530 assert_in_contract!(env);
531 let len: u32 = N.try_into().unwrap_optimized();
532 let obj = internal::Env::prng_bytes_new(env, len.into()).unwrap_infallible();
533 unsafe { BytesN::unchecked_new(env.clone(), obj) }
534 }
535}
536
537impl Fill for [u8] {
538 /// Fills the slice with the Prng.
539 ///
540 /// # Panics
541 ///
542 /// If the slice is greater than u32::MAX in length.
543 fn fill(&mut self, prng: &Prng) {
544 let env = prng.env();
545 assert_in_contract!(env);
546 let len: u32 = self.len().try_into().unwrap_optimized();
547 let bytes: Bytes = internal::Env::prng_bytes_new(env, len.into())
548 .unwrap_infallible()
549 .into_val(env);
550 bytes.copy_into_slice(self);
551 }
552}
553
554impl<const N: usize> Fill for [u8; N] {
555 /// Fills the array with the Prng.
556 ///
557 /// # Panics
558 ///
559 /// If the array is greater than u32::MAX in length.
560 fn fill(&mut self, prng: &Prng) {
561 let env = prng.env();
562 assert_in_contract!(env);
563 let len: u32 = N.try_into().unwrap_optimized();
564 let bytes: Bytes = internal::Env::prng_bytes_new(env, len.into())
565 .unwrap_infallible()
566 .into_val(env);
567 bytes.copy_into_slice(self);
568 }
569}
570
571impl<const N: usize> Gen for [u8; N] {
572 /// Generates the array with the Prng.
573 ///
574 /// # Panics
575 ///
576 /// If the array is greater than u32::MAX in length.
577 fn gen(prng: &Prng) -> Self {
578 let mut v = [0u8; N];
579 v.fill(prng);
580 v
581 }
582}