diff --git a/CHANGELOG.md b/CHANGELOG.md index aa33cb3695..47239e9d0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ The `dfx cycles` command no longer needs nor accepts the `--cycles-ledger-canist Updated to candid 0.10, ic-cdk 0.12, and ic-cdk-timers 0.6 +### fix: store playground canister acquisition timestamps with nanosecond precision on all platforms + +They've always been stored with nanosecond precisions on Linux and Macos. +Now they are stored with nanosecond precision on Windows too. + # 0.15.3 ### fix: allow `http://localhost:*` as `connect-src` in the asset canister's CSP diff --git a/src/dfx-core/src/config/model/canister_id_store.rs b/src/dfx-core/src/config/model/canister_id_store.rs index 76485bef5a..68693ac0f1 100644 --- a/src/dfx-core/src/config/model/canister_id_store.rs +++ b/src/dfx-core/src/config/model/canister_id_store.rs @@ -23,11 +23,14 @@ pub type CanisterIds = BTreeMap; pub type CanisterTimestamps = BTreeMap; +// OffsetDateTime has nanosecond precision, while SystemTime is OS-dependent (100ns on Windows) +pub type AcquisitionDateTime = OffsetDateTime; + #[derive(Debug, Clone, Default)] -pub struct NetworkNametoCanisterTimestamp(BTreeMap); +pub struct NetworkNametoCanisterTimestamp(BTreeMap); impl Deref for NetworkNametoCanisterTimestamp { - type Target = BTreeMap; + type Target = BTreeMap; fn deref(&self) -> &Self::Target { &self.0 @@ -48,7 +51,7 @@ impl Serialize for NetworkNametoCanisterTimestamp { let out = self.0.iter().map(|(key, time)| { ( key, - OffsetDateTime::from(*time) + AcquisitionDateTime::from(*time) .format(&Rfc3339) .expect("Failed to serialise timestamp"), ) @@ -63,12 +66,12 @@ impl<'de> Deserialize<'de> for NetworkNametoCanisterTimestamp { D: serde::Deserializer<'de>, { let map: BTreeMap = Deserialize::deserialize(deserializer)?; - let btree: BTreeMap = map + let btree: BTreeMap = map .into_iter() - .map(|(key, timestamp)| (key, OffsetDateTime::parse(×tamp, &Rfc3339))) + .map(|(key, timestamp)| (key, AcquisitionDateTime::parse(×tamp, &Rfc3339))) .try_fold(BTreeMap::new(), |mut map, (key, result)| match result { Ok(value) => { - map.insert(key, SystemTime::from(value)); + map.insert(key, value); Ok(map) } Err(err) => Err(err), @@ -178,7 +181,7 @@ impl CanisterIdStore { Ok(store) } - pub fn get_timestamp(&self, canister_name: &str) -> Option<&SystemTime> { + pub fn get_timestamp(&self, canister_name: &str) -> Option<&AcquisitionDateTime> { self.acquisition_timestamps .get(canister_name) .and_then(|timestamp_map| timestamp_map.get(&self.network_descriptor.name)) @@ -277,7 +280,7 @@ impl CanisterIdStore { &mut self, canister_name: &str, canister_id: &str, - timestamp: Option, + timestamp: Option, ) -> Result<(), CanisterIdStoreError> { let network_name = &self.network_descriptor.name; match self.ids.get_mut(canister_name) { diff --git a/src/dfx/src/lib/operations/canister/motoko_playground.rs b/src/dfx/src/lib/operations/canister/motoko_playground.rs index e9f4d33dd4..8e8f9ea506 100644 --- a/src/dfx/src/lib/operations/canister/motoko_playground.rs +++ b/src/dfx/src/lib/operations/canister/motoko_playground.rs @@ -1,17 +1,16 @@ +use crate::lib::{environment::Environment, error::DfxResult}; +use anyhow::{bail, Context}; +use candid::{encode_args, CandidType, Decode, Deserialize, Encode, Principal}; +use dfx_core::config::model::canister_id_store::AcquisitionDateTime; use dfx_core::config::model::network_descriptor::{ NetworkTypeDescriptor, MAINNET_MOTOKO_PLAYGROUND_CANISTER_ID, }; -use num_traits::ToPrimitive; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; - -use anyhow::{bail, Context}; -use candid::{encode_args, CandidType, Decode, Deserialize, Encode, Principal}; use fn_error_context::context; use ic_utils::interfaces::management_canister::builders::InstallMode; +use num_traits::ToPrimitive; use rand::Rng; use slog::{debug, info}; - -use crate::lib::{environment::Environment, error::DfxResult}; +use std::time::SystemTime; /// Arguments for the `getCanisterId` call. #[derive(CandidType)] @@ -28,19 +27,17 @@ pub struct CanisterInfo { } impl CanisterInfo { - #[context("Failed to construct playground canister info.")] - pub fn from(id: Principal, timestamp: &SystemTime) -> DfxResult { - let timestamp = candid::Int::from(timestamp.duration_since(UNIX_EPOCH)?.as_nanos()); - Ok(Self { id, timestamp }) + pub fn from(id: Principal, timestamp: &AcquisitionDateTime) -> Self { + let timestamp = candid::Int::from(timestamp.unix_timestamp_nanos()); + Self { id, timestamp } } - #[context("Failed to turn CanisterInfo into SystemTime")] - pub fn get_timestamp(&self) -> DfxResult { - UNIX_EPOCH - .checked_add(Duration::from_nanos( - self.timestamp.0.to_u64().context("u64 overflow")?, - )) - .context("Failed to make absolute time from offset") + #[context("Failed to get timestamp from CanisterInfo")] + pub fn get_timestamp(&self) -> DfxResult { + AcquisitionDateTime::from_unix_timestamp_nanos( + self.timestamp.0.to_i128().context("i128 overflow")?, + ) + .context("Failed to make unix timestamp from nanos") } } @@ -122,7 +119,7 @@ pub async fn reserve_canister_with_playground( pub async fn authorize_asset_uploader( env: &dyn Environment, canister_id: Principal, - canister_timestamp: &SystemTime, + canister_timestamp: &AcquisitionDateTime, principal_to_authorize: &Principal, ) -> DfxResult { let agent = env.get_agent(); @@ -135,7 +132,7 @@ pub async fn authorize_asset_uploader( } else { bail!("Trying to authorize asset uploader on non-playground network.") }; - let canister_info = CanisterInfo::from(canister_id, canister_timestamp)?; + let canister_info = CanisterInfo::from(canister_id, canister_timestamp); let nested_arg = Encode!(&principal_to_authorize)?; let call_arg = Encode!(&canister_info, &"authorize", &nested_arg)?; @@ -152,13 +149,13 @@ pub async fn authorize_asset_uploader( pub async fn playground_install_code( env: &dyn Environment, canister_id: Principal, - canister_timestamp: &SystemTime, + canister_timestamp: &AcquisitionDateTime, arg: &[u8], wasm_module: &[u8], mode: InstallMode, is_asset_canister: bool, -) -> DfxResult { - let canister_info = CanisterInfo::from(canister_id, canister_timestamp)?; +) -> DfxResult { + let canister_info = CanisterInfo::from(canister_id, canister_timestamp); let agent = env.get_agent(); let playground_canister = match env.get_network_descriptor().r#type { NetworkTypeDescriptor::Playground {