-
Notifications
You must be signed in to change notification settings - Fork 337
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
fd7cd5a
commit d17ee31
Showing
9 changed files
with
341 additions
and
133 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
use std::{collections::HashSet, time::Duration}; | ||
|
||
use crate::{ | ||
logs::P0, | ||
metrics::METRICS, | ||
storage::{StorableSalt, API_BOUNDARY_NODE_PRINCIPALS, SALT, SALT_SIZE}, | ||
time::delay_till_next_month, | ||
}; | ||
use candid::Principal; | ||
use ic_canister_log::log; | ||
use ic_cdk::{api::time, call, spawn}; | ||
use ic_cdk_timers::{set_timer, set_timer_interval}; | ||
use ic_nns_constants::REGISTRY_CANISTER_ID; | ||
use salt_api::{ | ||
ApiBoundaryNodeIdRecord, GetApiBoundaryNodeIdsRequest, InitArg, SaltGenerationStrategy, | ||
}; | ||
|
||
const REGISTRY_CANISTER_METHOD: &str = "get_api_boundary_node_ids"; | ||
|
||
pub async fn init_async(init_arg: InitArg) { | ||
if !is_salt_init() || init_arg.regenerate_now { | ||
if let Err(err) = try_regenerate_salt().await { | ||
log!(P0, "[init_regenerate_salt_failed]: {err}"); | ||
} | ||
} | ||
// Start salt generation schedule based on the argument. | ||
match init_arg.salt_generation_strategy { | ||
SaltGenerationStrategy::StartOfMonth => schedule_monthly_salt_generation(), | ||
} | ||
// Periodically poll API boundary nodes | ||
let period = Duration::from_secs(init_arg.registry_polling_interval_secs); | ||
set_timer_interval(period, || spawn(poll_api_boundary_nodes())); | ||
} | ||
|
||
// Sets an execution timer (delayed future task) and returns immediately. | ||
pub fn schedule_monthly_salt_generation() { | ||
let delay = delay_till_next_month(time()); | ||
set_timer(delay, || { | ||
spawn(async { | ||
if let Err(err) = try_regenerate_salt().await { | ||
log!(P0, "[scheduled_regenerate_salt_failed]: {err}"); | ||
} | ||
// Function is called recursively to schedule next execution | ||
schedule_monthly_salt_generation(); | ||
}); | ||
}); | ||
} | ||
|
||
pub fn is_salt_init() -> bool { | ||
SALT.with(|cell| cell.borrow().get(&())).is_some() | ||
} | ||
|
||
// Regenerate salt and store it in the stable memory | ||
// Can only fail, if the calls to management canister fail. | ||
pub async fn try_regenerate_salt() -> Result<(), String> { | ||
// Closure for getting random bytes from the IC. | ||
let rnd_call = |attempt: u32| async move { | ||
ic_cdk::call(Principal::management_canister(), "raw_rand", ()) | ||
.await | ||
.map_err(|err| { | ||
format!( | ||
"Call {attempt} to raw_rand failed: code={:?}, err={}", | ||
err.0, err.1 | ||
) | ||
}) | ||
}; | ||
|
||
let (rnd_bytes_1,): ([u8; 32],) = rnd_call(1).await?; | ||
let (rnd_bytes_2,): ([u8; 32],) = rnd_call(2).await?; | ||
|
||
// Concatenate arrays to form an array of 64 random bytes. | ||
let mut salt = [rnd_bytes_1, rnd_bytes_2].concat(); | ||
salt.truncate(SALT_SIZE); | ||
|
||
let stored_salt = StorableSalt { | ||
salt, | ||
salt_id: time(), | ||
}; | ||
|
||
SALT.with(|cell| { | ||
cell.borrow_mut().insert((), stored_salt); | ||
}); | ||
|
||
Ok(()) | ||
} | ||
|
||
pub async fn poll_api_boundary_nodes() { | ||
let canister_id = Principal::from(REGISTRY_CANISTER_ID); | ||
|
||
let (call_status, message) = match call::<_, (Result<Vec<ApiBoundaryNodeIdRecord>, String>,)>( | ||
canister_id, | ||
REGISTRY_CANISTER_METHOD, | ||
(&GetApiBoundaryNodeIdsRequest {},), | ||
) | ||
.await | ||
{ | ||
Ok((Ok(api_bn_records),)) => { | ||
// Set authorized readers of salt. | ||
let principals: HashSet<_> = api_bn_records.into_iter().filter_map(|n| n.id).collect(); | ||
API_BOUNDARY_NODE_PRINCIPALS.with(|cell| *cell.borrow_mut() = principals); | ||
// Update metric. | ||
let current_time = time() as i64; | ||
METRICS.with(|cell| { | ||
cell.borrow_mut() | ||
.last_successful_registry_poll_time | ||
.set(current_time); | ||
}); | ||
("success", "") | ||
} | ||
Ok((Err(err),)) => { | ||
log!( | ||
P0, | ||
"[poll_api_boundary_nodes]: failed to fetch nodes from registry {err:?}", | ||
); | ||
("failure", "calling_canister_method_failed") | ||
} | ||
Err(err) => { | ||
log!( | ||
P0, | ||
"[poll_api_boundary_nodes]: failed to fetch nodes from registry {err:?}", | ||
); | ||
("failure", "canister_call_rejected") | ||
} | ||
}; | ||
// Update metric. | ||
METRICS.with(|cell| { | ||
cell.borrow_mut() | ||
.registry_poll_calls | ||
.with_label_values(&[call_status, message]) | ||
.inc(); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.