1use crate::{
4 config::schema::{RandomnessConfig, RandomnessSource},
5 log,
6 log::Topic,
7 ops::{
8 config::ConfigOps,
9 ic::{mgmt, timer::TimerOps},
10 },
11};
12use canic_utils::rand as rand_utils;
13use sha2::{Digest, Sha256};
14use std::time::Duration;
15
16pub struct RandomOps;
26
27impl RandomOps {
28 pub fn start() {
30 let cfg = Self::randomness_config();
31 if !cfg.enabled {
32 crate::log!(Topic::Init, Info, "randomness seeding disabled by config");
33 return;
34 }
35
36 let interval_secs = cfg.reseed_interval_secs;
37 if interval_secs == 0 {
38 crate::log!(
39 Topic::Init,
40 Warn,
41 "randomness reseed_interval_secs is 0; seeding disabled"
42 );
43 return;
44 }
45
46 let interval = Duration::from_secs(interval_secs);
47 Self::schedule_seeding(Duration::ZERO, interval, cfg.source);
48 }
49
50 async fn seed_once(source: RandomnessSource) {
51 match source {
52 RandomnessSource::Ic => match mgmt::raw_rand().await {
53 Ok(seed) => {
54 log!(Topic::Init, Warn, "seed_from({seed:?})");
55 rand_utils::seed_from(seed);
56 }
57 Err(err) => {
58 crate::log!(Topic::Init, Warn, "raw_rand reseed failed: {err}");
59 }
60 },
61 RandomnessSource::Time => Self::seed_from_time(),
62 }
63 }
64
65 fn schedule_seeding(delay: Duration, interval: Duration, source: RandomnessSource) {
66 let _ = TimerOps::set(delay, "random:seed", async move {
67 Self::seed_once(source).await;
68 Self::schedule_seeding(interval, interval, source);
69 });
70 }
71
72 fn randomness_config() -> RandomnessConfig {
73 match ConfigOps::current_canister() {
74 Ok(cfg) => cfg.randomness,
75 Err(err) => {
76 crate::log!(
77 Topic::Init,
78 Warn,
79 "randomness config unavailable, using defaults: {err}"
80 );
81 RandomnessConfig::default()
82 }
83 }
84 }
85
86 fn seed_from_time() {
87 let now = crate::cdk::api::time();
88 let canister_id = crate::cdk::api::canister_self();
89
90 let mut hasher = Sha256::new();
91 hasher.update(now.to_be_bytes());
92 hasher.update(canister_id.as_slice());
93 let seed: [u8; 32] = hasher.finalize().into();
94
95 rand_utils::seed_from(seed);
96 }
97}