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}