feat: add HoloLake persona time authority
This commit is contained in:
parent
a2fb2760f8
commit
a96c5955bb
7 changed files with 827 additions and 18 deletions
|
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
"schema": "hololake.persona-time-authority-contract/v1",
|
||||
"record_id": "HLP-PERSONA-TIME-AUTHORITY-001",
|
||||
"formal_name": "光湖人格时间主控系统",
|
||||
"era_name": "曜冥纪元",
|
||||
"calendar_name": "光湖历",
|
||||
"state": "NATIVE_SOURCE_IMPLEMENTED_AND_TESTED_CLOCK_NETWORK_ATTESTATION_PENDING",
|
||||
"reality_time": {
|
||||
"canonical_zone": "Asia/Shanghai",
|
||||
"utc_offset": "+08:00",
|
||||
"display_name": "北京时间",
|
||||
"continues_while_application_is_closed": true,
|
||||
"process_uptime_is_time_source": false,
|
||||
"current_source": "HOST_OPERATING_SYSTEM_REALTIME_CLOCK",
|
||||
"current_verification": "LOCAL_CLOCK_NOT_NETWORK_ATTESTED",
|
||||
"network_attested_source_required_for_verified_reality_time": true
|
||||
},
|
||||
"guanghu_era": {
|
||||
"epoch_date": "2025-04-26",
|
||||
"epoch_day": 1,
|
||||
"epoch_exact_time": null,
|
||||
"epoch_precision": "DAY_ONLY_EXACT_TIME_UNKNOWN",
|
||||
"world_day_formula": "BEIJING_CIVIL_DATE_MINUS_2025_04_26_PLUS_ONE",
|
||||
"millisecond_precision_transition_date": "2026-08-17",
|
||||
"millisecond_chain_origin": "FIRST_DURABLE_TIME_TICKET"
|
||||
},
|
||||
"ticket": {
|
||||
"schema": "hololake.persona-time-ticket/v1",
|
||||
"uniqueness_scope": "ONE_DURABLE_LOCAL_AUTHORITY_PER_AUTHENTICATED_HUMAN_ACCOUNT",
|
||||
"components": [
|
||||
"AUTHORITY_ID",
|
||||
"BEIJING_REALITY_TIME",
|
||||
"LOGICAL_COLLISION_COUNTER",
|
||||
"MONOTONIC_ISSUANCE_SEQUENCE"
|
||||
],
|
||||
"atomic_storage": "SQLITE_IMMEDIATE_TRANSACTION_SYNCHRONOUS_FULL",
|
||||
"idempotent_request_id": true,
|
||||
"survives_restart": true,
|
||||
"clock_rollback_never_reverses_issued_time": true,
|
||||
"previous_ticket_chain": true,
|
||||
"receipt_sha256": true
|
||||
},
|
||||
"event_coordinate": {
|
||||
"human_controller": "AUTHENTICATED_DIRECT_SESSION_ACCOUNT",
|
||||
"channel": "AUTHENTICATED_DIRECT_SESSION_LANE",
|
||||
"client_instance": "AUTHENTICATED_DIRECT_SESSION_INSTANCE",
|
||||
"persona_current_verification": "UNVERIFIED_CALLER_CLAIM",
|
||||
"host_software_current_verification": "UNVERIFIED_CALLER_CLAIM",
|
||||
"persona_and_host_upgrade_requires": "REGISTERED_BINDING_EVIDENCE"
|
||||
},
|
||||
"entries": {
|
||||
"tauri_commands": ["get_beijing_time_coordinate", "issue_persona_time_ticket"],
|
||||
"direct_local_broker_operations": ["GET_BEIJING_TIME", "ISSUE_PERSONA_TIME_TICKET"],
|
||||
"external_ticket_issue_requires_authenticated_non_visitor_session": true
|
||||
},
|
||||
"truth_boundary": {
|
||||
"source_implemented": true,
|
||||
"rust_tests_passed": true,
|
||||
"installed_runtime_acceptance": false,
|
||||
"network_clock_attestation": false,
|
||||
"persona_binding_runtime": false,
|
||||
"host_software_binding_runtime": false,
|
||||
"world_lighthouse_authority_registration": false
|
||||
},
|
||||
"historical_sources": [
|
||||
"REPO-012:zero-point/core-channel/YAOMING-NUMBERING-SYSTEM.hdlp",
|
||||
"REPO-012:gls/GLS-0235-GUANGHU-LANGUAGE-PERSONA-OS-DOMAIN-ROUTING-AND-KNOWLEDGE-PROJECTION.hdlp"
|
||||
]
|
||||
}
|
||||
|
|
@ -37,6 +37,7 @@
|
|||
"TCS_LANGUAGE_CONTRACT",
|
||||
"HOST_CAPABILITY_RECEIPTS",
|
||||
"EVENT_AND_LAKE_LAMP",
|
||||
"PERSONA_TIME_AUTHORITY",
|
||||
"MEMORY_GIT_EVIDENCE",
|
||||
"KNOWLEDGE_PROJECTION",
|
||||
"HUMAN_APPROVAL_CENTER",
|
||||
|
|
@ -112,6 +113,16 @@
|
|||
"public_developer_id_and_notarization": false,
|
||||
"server_deployment": false
|
||||
},
|
||||
"persona_time_authority": {
|
||||
"contract": "contracts/persona-time-authority.json",
|
||||
"native_source_implemented": true,
|
||||
"direct_local_broker_integrated": true,
|
||||
"beijing_reality_time_projection": true,
|
||||
"guanghu_era_day_projection": true,
|
||||
"durable_unique_ticket_runtime": true,
|
||||
"network_clock_attestation": false,
|
||||
"installed_runtime_acceptance": false
|
||||
},
|
||||
"reality_mutation_requires": ["VERIFIED_HUMAN_SUBJECT", "EXACT_ACTION", "EXACT_TARGET", "IMMUTABLE_PAYLOAD_DIGEST", "EXPIRY", "REPLAY_PROTECTION", "EXECUTION_RECEIPT", "READBACK_RECEIPT"],
|
||||
"context_risk_signals": ["MODEL_CONTEXT_SPEC", "ESTIMATED_TOKENS", "MESSAGE_VOLUME", "TURN_COUNT", "TASK_STAGE", "HOST_WARNING"],
|
||||
"exact_host_compaction_prediction_claimed": false,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import test from 'node:test'
|
||||
|
||||
const read = (relative) => fs.readFileSync(new URL(`../${relative}`, import.meta.url), 'utf8')
|
||||
const contract = JSON.parse(read('contracts/persona-time-authority.json'))
|
||||
const stageOne = JSON.parse(read('contracts/stage-one-platform.json'))
|
||||
const rust = read('src-tauri/src/persona_time_authority.rs')
|
||||
const broker = read('src-tauri/src/direct_local_broker.rs')
|
||||
const lib = read('src-tauri/src/lib.rs')
|
||||
|
||||
test('Guanghu era is anchored to Beijing reality time without inventing an exact historical instant', () => {
|
||||
assert.equal(contract.era_name, '曜冥纪元')
|
||||
assert.equal(contract.calendar_name, '光湖历')
|
||||
assert.equal(contract.reality_time.canonical_zone, 'Asia/Shanghai')
|
||||
assert.equal(contract.reality_time.continues_while_application_is_closed, true)
|
||||
assert.equal(contract.guanghu_era.epoch_date, '2025-04-26')
|
||||
assert.equal(contract.guanghu_era.epoch_day, 1)
|
||||
assert.equal(contract.guanghu_era.epoch_exact_time, null)
|
||||
assert.equal(contract.guanghu_era.epoch_precision, 'DAY_ONLY_EXACT_TIME_UNKNOWN')
|
||||
assert.equal(contract.guanghu_era.millisecond_precision_transition_date, '2026-08-17')
|
||||
})
|
||||
|
||||
test('persona time tickets are durable unique and available to authenticated local carriers', () => {
|
||||
assert.equal(contract.ticket.atomic_storage, 'SQLITE_IMMEDIATE_TRANSACTION_SYNCHRONOUS_FULL')
|
||||
assert.equal(contract.ticket.idempotent_request_id, true)
|
||||
assert.equal(contract.ticket.clock_rollback_never_reverses_issued_time, true)
|
||||
assert.equal(contract.entries.external_ticket_issue_requires_authenticated_non_visitor_session, true)
|
||||
assert.match(rust, /TransactionBehavior::Immediate/)
|
||||
assert.match(rust, /logical_counter/)
|
||||
assert.match(rust, /millisecond_chain_origin_ticket_id/)
|
||||
assert.match(broker, /GetBeijingTime/)
|
||||
assert.match(broker, /IssuePersonaTimeTicket/)
|
||||
assert.match(lib, /persona_time_authority::get_beijing_time_coordinate/)
|
||||
assert.match(lib, /persona_time_authority::issue_persona_time_ticket/)
|
||||
})
|
||||
|
||||
test('unverified clock persona and host claims remain explicit truth boundaries', () => {
|
||||
assert.equal(contract.reality_time.current_verification, 'LOCAL_CLOCK_NOT_NETWORK_ATTESTED')
|
||||
assert.equal(contract.event_coordinate.persona_current_verification, 'UNVERIFIED_CALLER_CLAIM')
|
||||
assert.equal(contract.event_coordinate.host_software_current_verification, 'UNVERIFIED_CALLER_CLAIM')
|
||||
assert.equal(contract.truth_boundary.installed_runtime_acceptance, false)
|
||||
assert.equal(contract.truth_boundary.world_lighthouse_authority_registration, false)
|
||||
assert.equal(stageOne.persona_time_authority.network_clock_attestation, false)
|
||||
assert.equal(stageOne.persona_time_authority.installed_runtime_acceptance, false)
|
||||
})
|
||||
|
|
@ -9,6 +9,9 @@ use crate::dynamic_capability_routing::{
|
|||
routing_root as dynamic_routing_root, DynamicNodeRegistry, ResolveCapabilityRouteInput,
|
||||
SignedNodeHealth,
|
||||
};
|
||||
use crate::persona_time_authority::{
|
||||
beijing_time_coordinate, issue_authenticated_at, IssuePersonaTimeTicketInput,
|
||||
};
|
||||
use crate::pncc_receipt_projection::{
|
||||
append_remote_read_at as project_pncc_remote_read_at,
|
||||
append_repository_binding_at as project_pncc_repository_binding_at,
|
||||
|
|
@ -115,6 +118,7 @@ struct BrokerStorageRoots {
|
|||
pncc_remote: PathBuf,
|
||||
pncc_projection: PathBuf,
|
||||
language_inbox: PathBuf,
|
||||
persona_time: PathBuf,
|
||||
}
|
||||
|
||||
impl Drop for DirectLocalBrokerHandle {
|
||||
|
|
@ -148,6 +152,8 @@ enum BrokerRequest {
|
|||
InspectMountedPnccRepository(AuthenticatedPnccMountInput),
|
||||
ReadMountedPnccRemoteObject(AuthenticatedPnccRemoteReadInput),
|
||||
QueryPnccReceiptProjection(AuthenticatedPnccProjectionQueryInput),
|
||||
GetBeijingTime,
|
||||
IssuePersonaTimeTicket(IssuePersonaTimeTicketInput),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
|
|
@ -398,6 +404,10 @@ fn start_at(
|
|||
.parent()
|
||||
.ok_or("HOLOLAKE_BROKER_LANGUAGE_STORAGE_BOUNDARY_INVALID")?
|
||||
.join("circular-lake-language-inbox-v1");
|
||||
let persona_time_root = session_root
|
||||
.parent()
|
||||
.ok_or("HOLOLAKE_BROKER_PERSONA_TIME_STORAGE_BOUNDARY_INVALID")?
|
||||
.join("persona-time-authority-v1");
|
||||
if let Some(parent) = socket_path.parent() {
|
||||
fs::create_dir_all(parent)
|
||||
.map_err(|error| format!("HOLOLAKE_BROKER_RUNTIME_DIR_FAILED: {error}"))?;
|
||||
|
|
@ -452,6 +462,7 @@ fn start_at(
|
|||
pncc_remote: pncc_remote_root,
|
||||
pncc_projection: pncc_projection_root,
|
||||
language_inbox: language_inbox_root,
|
||||
persona_time: persona_time_root,
|
||||
},
|
||||
&worker_shutdown,
|
||||
&worker_authenticated_connections,
|
||||
|
|
@ -530,15 +541,7 @@ fn serve_connection(
|
|||
let response = if read as u64 > MAX_REQUEST_BYTES || !bytes.ends_with(b"\n") {
|
||||
BrokerResponse::error("HOLOLAKE_BROKER_REQUEST_TOO_LARGE")
|
||||
} else {
|
||||
dispatch(
|
||||
&roots.session,
|
||||
&roots.routing,
|
||||
&roots.pncc_mount,
|
||||
&roots.pncc_remote,
|
||||
&roots.pncc_projection,
|
||||
&roots.language_inbox,
|
||||
&bytes[..bytes.len() - 1],
|
||||
)
|
||||
dispatch(roots, &bytes[..bytes.len() - 1])
|
||||
};
|
||||
if response.ok && request_authenticates && authenticated_connection.is_none() {
|
||||
authenticated_connections.fetch_add(1, Ordering::AcqRel);
|
||||
|
|
@ -575,15 +578,14 @@ fn request_establishes_authenticated_connection(bytes: &[u8]) -> bool {
|
|||
})
|
||||
}
|
||||
|
||||
fn dispatch(
|
||||
session_root: &Path,
|
||||
routing_root: &Path,
|
||||
pncc_mount_root: &Path,
|
||||
pncc_remote_root: &Path,
|
||||
pncc_projection_root: &Path,
|
||||
language_inbox_root: &Path,
|
||||
bytes: &[u8],
|
||||
) -> BrokerResponse {
|
||||
fn dispatch(roots: &BrokerStorageRoots, bytes: &[u8]) -> BrokerResponse {
|
||||
let session_root = &roots.session;
|
||||
let routing_root = &roots.routing;
|
||||
let pncc_mount_root = &roots.pncc_mount;
|
||||
let pncc_remote_root = &roots.pncc_remote;
|
||||
let pncc_projection_root = &roots.pncc_projection;
|
||||
let language_inbox_root = &roots.language_inbox;
|
||||
let persona_time_root = &roots.persona_time;
|
||||
let request: BrokerRequest = match serde_json::from_slice(bytes) {
|
||||
Ok(request) => request,
|
||||
Err(error) => {
|
||||
|
|
@ -668,6 +670,18 @@ fn dispatch(
|
|||
serde_json::to_value(receipt).map_err(|error| error.to_string())
|
||||
})
|
||||
}
|
||||
BrokerRequest::GetBeijingTime => std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|duration| duration.as_millis() as u64)
|
||||
.map_err(|error| format!("HOLOLAKE_SYSTEM_CLOCK_INVALID: {error}"))
|
||||
.and_then(beijing_time_coordinate)
|
||||
.and_then(|coordinate| {
|
||||
serde_json::to_value(coordinate).map_err(|error| error.to_string())
|
||||
}),
|
||||
BrokerRequest::IssuePersonaTimeTicket(input) => {
|
||||
issue_authenticated_at(persona_time_root, session_root, input)
|
||||
.and_then(|ticket| serde_json::to_value(ticket).map_err(|error| error.to_string()))
|
||||
}
|
||||
};
|
||||
match result {
|
||||
Ok(value) => BrokerResponse::success(value),
|
||||
|
|
@ -840,6 +854,89 @@ mod tests {
|
|||
assert_eq!(resumed["result"]["state"], "RESUMED");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn broker_projects_beijing_time_and_issues_a_ticket_only_to_an_authenticated_session() {
|
||||
let temp = TempDir::new().unwrap();
|
||||
let socket = temp.path().join("runtime/broker.sock");
|
||||
let descriptor = temp.path().join("broker.json");
|
||||
let sessions = temp
|
||||
.path()
|
||||
.join("accounts-v1/test-account/direct-local-session-v1");
|
||||
let routes = temp.path().join("routes");
|
||||
fs::create_dir_all(&sessions).unwrap();
|
||||
fs::create_dir_all(&routes).unwrap();
|
||||
let _broker = start_at(sessions.clone(), routes, descriptor, socket.clone()).unwrap();
|
||||
|
||||
let clock = request(
|
||||
&socket,
|
||||
serde_json::json!({ "operation": "GET_BEIJING_TIME" }),
|
||||
);
|
||||
assert_eq!(clock["ok"], true);
|
||||
assert_eq!(clock["result"]["timeZone"], "Asia/Shanghai (UTC+08:00)");
|
||||
assert_eq!(clock["result"]["guanghuEpochDate"], "2025-04-26");
|
||||
|
||||
let unauthenticated = request(
|
||||
&socket,
|
||||
serde_json::json!({
|
||||
"operation": "ISSUE_PERSONA_TIME_TICKET",
|
||||
"input": {
|
||||
"session": {
|
||||
"accountId": "human-1",
|
||||
"sessionId": "invented-session",
|
||||
"resumeSecret": "invented-secret-long-enough"
|
||||
},
|
||||
"requestId": "time-request-unauthenticated",
|
||||
"personaId": "ICE-P-ZY001",
|
||||
"hostSoftwareId": "HOST-CODEX-MACOS-001"
|
||||
}
|
||||
}),
|
||||
);
|
||||
assert_eq!(unauthenticated["ok"], false);
|
||||
|
||||
let discovery = issue_ticket_at(
|
||||
&sessions,
|
||||
IssueDiscoveryTicketInput {
|
||||
account_id: "human-1".into(),
|
||||
lane_id: "codex-current-channel".into(),
|
||||
client_instance_id: "codex-instance-1".into(),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let opened = request(
|
||||
&socket,
|
||||
serde_json::json!({
|
||||
"operation": "OPEN_SESSION",
|
||||
"input": {
|
||||
"accountId": "human-1",
|
||||
"laneId": "codex-current-channel",
|
||||
"clientInstanceId": "codex-instance-1",
|
||||
"discoveryTicket": discovery.discovery_ticket
|
||||
}
|
||||
}),
|
||||
);
|
||||
let issued = request(
|
||||
&socket,
|
||||
serde_json::json!({
|
||||
"operation": "ISSUE_PERSONA_TIME_TICKET",
|
||||
"input": {
|
||||
"session": {
|
||||
"accountId": "human-1",
|
||||
"sessionId": opened["result"]["sessionId"],
|
||||
"resumeSecret": opened["result"]["resumeSecret"]
|
||||
},
|
||||
"requestId": "time-request-1",
|
||||
"personaId": "ICE-P-ZY001",
|
||||
"hostSoftwareId": "HOST-CODEX-MACOS-001"
|
||||
}
|
||||
}),
|
||||
);
|
||||
assert_eq!(issued["ok"], true);
|
||||
assert_eq!(issued["result"]["channelId"], "codex-current-channel");
|
||||
assert_eq!(issued["result"]["clientInstanceId"], "codex-instance-1");
|
||||
assert_eq!(issued["result"]["humanControllerId"], "human-1");
|
||||
assert_eq!(issued["result"]["guanghuEpochDate"], "2025-04-26");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn broker_can_restart_and_resume_persisted_session() {
|
||||
let temp = TempDir::new().unwrap();
|
||||
|
|
|
|||
|
|
@ -70,6 +70,14 @@ pub struct AuthenticateSessionInput {
|
|||
pub resume_secret: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct AuthenticatedSessionContext {
|
||||
pub account_key: String,
|
||||
pub session_id: String,
|
||||
pub lane_id: String,
|
||||
pub client_instance_id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DirectSessionReceipt {
|
||||
|
|
@ -459,6 +467,35 @@ pub(crate) fn authenticate_with_lane_at(
|
|||
Ok(record.lane_id)
|
||||
}
|
||||
|
||||
pub(crate) fn authenticate_context_at(
|
||||
root: &Path,
|
||||
input: &AuthenticateSessionInput,
|
||||
) -> Result<AuthenticatedSessionContext, String> {
|
||||
validate_identifier(&input.account_id, "ACCOUNT")?;
|
||||
validate_identifier(&input.session_id, "SESSION")?;
|
||||
validate_secret(&input.resume_secret, "RESUME_SECRET")?;
|
||||
let account_key = sha256_hex(input.account_id.as_bytes());
|
||||
require_active_session(root, &account_key, &input.session_id)?;
|
||||
let path = session_path(root, &account_key, &input.session_id);
|
||||
let _lock = lock_session(&path)?;
|
||||
let record = read_session(&path)?;
|
||||
authorize(
|
||||
&record,
|
||||
&account_key,
|
||||
&input.session_id,
|
||||
&input.resume_secret,
|
||||
)?;
|
||||
if record.lane_id == "visitor-expression-only" {
|
||||
return Err("HOLOLAKE_VISITOR_SESSION_HAS_NO_SYSTEM_AUTHORITY".into());
|
||||
}
|
||||
Ok(AuthenticatedSessionContext {
|
||||
account_key,
|
||||
session_id: record.session_id,
|
||||
lane_id: record.lane_id,
|
||||
client_instance_id: record.client_instance_id,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn authenticate_privileged_at(
|
||||
root: &Path,
|
||||
input: &AuthenticateSessionInput,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ mod glp_envelope;
|
|||
mod home_status;
|
||||
mod knowledge_base;
|
||||
mod local_development_bridge;
|
||||
mod persona_time_authority;
|
||||
mod personal_channel;
|
||||
mod pncc_receipt_projection;
|
||||
mod pncc_remote_git;
|
||||
|
|
@ -53,6 +54,8 @@ pub fn run() {
|
|||
personal_channel::initialize_personal_channel,
|
||||
personal_channel::create_personal_channel_task,
|
||||
personal_channel::transition_personal_channel_task,
|
||||
persona_time_authority::issue_persona_time_ticket,
|
||||
persona_time_authority::get_beijing_time_coordinate,
|
||||
knowledge_base::get_knowledge_snapshot,
|
||||
knowledge_base::read_knowledge_document,
|
||||
knowledge_base::search_knowledge,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,546 @@
|
|||
//! 光湖人格时间主控:为已认证的同设备会话签发持久、唯一、可重放核验的事件坐标票。
|
||||
//!
|
||||
//! 物理时钟只是一项观测。正式唯一时间由本地权威编号、单调签发序列和
|
||||
//! 同毫秒逻辑序列共同组成;系统时钟回拨不会让已签发时间倒退。
|
||||
|
||||
use ring::digest::{digest, SHA256};
|
||||
use rusqlite::{params, Connection, OptionalExtension, TransactionBehavior};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use tauri::AppHandle;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::direct_local_session::{
|
||||
authenticate_context_at, direct_session_root, AuthenticateSessionInput,
|
||||
AuthenticatedSessionContext,
|
||||
};
|
||||
|
||||
const TICKET_SCHEMA: &str = "hololake.persona-time-ticket/v1";
|
||||
const MAX_ID_BYTES: usize = 160;
|
||||
const GUANGHU_EPOCH_DATE: &str = "2025-04-26";
|
||||
const GUANGHU_EPOCH_BEIJING_DAY_INDEX: i64 = 20_204;
|
||||
const MILLISECOND_PRECISION_TRANSITION_DATE: &str = "2026-08-17";
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct IssuePersonaTimeTicketInput {
|
||||
pub session: AuthenticateSessionInput,
|
||||
pub request_id: String,
|
||||
pub persona_id: String,
|
||||
pub host_software_id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PersonaTimeTicket {
|
||||
pub schema: String,
|
||||
pub state: String,
|
||||
pub authority_id: String,
|
||||
pub ticket_id: String,
|
||||
pub unique_timestamp: String,
|
||||
pub physical_unix_ms: u64,
|
||||
pub beijing_time: String,
|
||||
pub time_zone: String,
|
||||
pub clock_source: String,
|
||||
pub clock_verification: String,
|
||||
pub guanghu_epoch_date: String,
|
||||
pub guanghu_calendar_state: String,
|
||||
pub elapsed_beijing_dates_since_guanghu_epoch: i64,
|
||||
pub guanghu_era_day: i64,
|
||||
pub historical_epoch_precision: String,
|
||||
pub millisecond_precision_transition_date: String,
|
||||
pub millisecond_chain_origin_ticket_id: String,
|
||||
pub logical_counter: u64,
|
||||
pub issuance_sequence: u64,
|
||||
pub clock_rollback_observed: bool,
|
||||
pub human_controller_id: String,
|
||||
pub human_controller_verification: String,
|
||||
pub persona_id: String,
|
||||
pub persona_verification: String,
|
||||
pub host_software_id: String,
|
||||
pub host_software_verification: String,
|
||||
pub client_instance_id: String,
|
||||
pub channel_id: String,
|
||||
pub session_id: String,
|
||||
pub previous_ticket_id: Option<String>,
|
||||
pub request_id: String,
|
||||
pub receipt_sha256: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BeijingTimeCoordinate {
|
||||
pub schema: String,
|
||||
pub state: String,
|
||||
pub unix_ms: u64,
|
||||
pub beijing_time: String,
|
||||
pub time_zone: String,
|
||||
pub clock_source: String,
|
||||
pub clock_verification: String,
|
||||
pub continues_while_hololake_is_closed: bool,
|
||||
pub guanghu_epoch_date: String,
|
||||
pub guanghu_calendar_state: String,
|
||||
pub elapsed_beijing_dates_since_guanghu_epoch: i64,
|
||||
pub guanghu_era_day: i64,
|
||||
pub historical_epoch_precision: String,
|
||||
pub millisecond_precision_transition_date: String,
|
||||
pub millisecond_chain_state: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct VerifiedTicketRequest {
|
||||
pub request_id: String,
|
||||
pub human_controller_id: String,
|
||||
pub persona_id: String,
|
||||
pub host_software_id: String,
|
||||
pub session: AuthenticatedSessionContext,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn issue_persona_time_ticket(
|
||||
app: AppHandle,
|
||||
input: IssuePersonaTimeTicketInput,
|
||||
) -> Result<PersonaTimeTicket, String> {
|
||||
let session_root = direct_session_root(&app)?;
|
||||
let authority_root = authority_root(&app)?;
|
||||
tauri::async_runtime::spawn_blocking(move || {
|
||||
issue_authenticated_at(&authority_root, &session_root, input)
|
||||
})
|
||||
.await
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_JOIN_FAILED: {error}"))?
|
||||
}
|
||||
|
||||
pub(crate) fn issue_authenticated_at(
|
||||
authority_root: &Path,
|
||||
session_root: &Path,
|
||||
input: IssuePersonaTimeTicketInput,
|
||||
) -> Result<PersonaTimeTicket, String> {
|
||||
let session = authenticate_context_at(session_root, &input.session)?;
|
||||
issue_at(
|
||||
authority_root,
|
||||
VerifiedTicketRequest {
|
||||
request_id: input.request_id,
|
||||
human_controller_id: input.session.account_id,
|
||||
persona_id: input.persona_id,
|
||||
host_software_id: input.host_software_id,
|
||||
session,
|
||||
},
|
||||
now_unix_ms()?,
|
||||
)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_beijing_time_coordinate() -> Result<BeijingTimeCoordinate, String> {
|
||||
beijing_time_coordinate(now_unix_ms()?)
|
||||
}
|
||||
|
||||
pub(crate) fn beijing_time_coordinate(unix_ms: u64) -> Result<BeijingTimeCoordinate, String> {
|
||||
let elapsed_dates = beijing_day_index(unix_ms) - GUANGHU_EPOCH_BEIJING_DAY_INDEX;
|
||||
Ok(BeijingTimeCoordinate {
|
||||
schema: "hololake.beijing-time-coordinate/v1".into(),
|
||||
state: "FLOWING_REALITY_TIME".into(),
|
||||
unix_ms,
|
||||
beijing_time: format_beijing_time(unix_ms)?,
|
||||
time_zone: "Asia/Shanghai (UTC+08:00)".into(),
|
||||
clock_source: "HOST_OPERATING_SYSTEM_REALTIME_CLOCK".into(),
|
||||
clock_verification: "LOCAL_CLOCK_NOT_NETWORK_ATTESTED".into(),
|
||||
continues_while_hololake_is_closed: true,
|
||||
guanghu_epoch_date: GUANGHU_EPOCH_DATE.into(),
|
||||
guanghu_calendar_state: "EPOCH_DATE_LOCKED_EXACT_INSTANT_PENDING".into(),
|
||||
elapsed_beijing_dates_since_guanghu_epoch: elapsed_dates,
|
||||
guanghu_era_day: elapsed_dates + 1,
|
||||
historical_epoch_precision: "DAY_ONLY_EXACT_TIME_UNKNOWN".into(),
|
||||
millisecond_precision_transition_date: MILLISECOND_PRECISION_TRANSITION_DATE.into(),
|
||||
millisecond_chain_state: "BEGINS_AT_FIRST_DURABLE_TIME_TICKET".into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn authority_root(app: &AppHandle) -> Result<PathBuf, String> {
|
||||
crate::authenticated_storage::account_storage_root(app, "persona-time-authority-v1")
|
||||
}
|
||||
|
||||
pub(crate) fn issue_at(
|
||||
root: &Path,
|
||||
request: VerifiedTicketRequest,
|
||||
observed_unix_ms: u64,
|
||||
) -> Result<PersonaTimeTicket, String> {
|
||||
validate_request(&request)?;
|
||||
fs::create_dir_all(root)
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_STORAGE_UNAVAILABLE: {error}"))?;
|
||||
let database_path = root.join("authority.sqlite3");
|
||||
let mut connection = Connection::open(database_path)
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_STORAGE_UNAVAILABLE: {error}"))?;
|
||||
connection
|
||||
.busy_timeout(std::time::Duration::from_secs(5))
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_STORAGE_UNAVAILABLE: {error}"))?;
|
||||
initialize(&connection)?;
|
||||
let transaction = connection
|
||||
.transaction_with_behavior(TransactionBehavior::Immediate)
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_LOCK_FAILED: {error}"))?;
|
||||
|
||||
let request_digest = request_digest(&request);
|
||||
if let Some((stored_digest, receipt_json)) = transaction
|
||||
.query_row(
|
||||
"SELECT request_digest, receipt_json FROM tickets WHERE request_id = ?1",
|
||||
params![request.request_id],
|
||||
|row| Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?)),
|
||||
)
|
||||
.optional()
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_READ_FAILED: {error}"))?
|
||||
{
|
||||
if stored_digest != request_digest {
|
||||
return Err("HOLOLAKE_PERSONA_TIME_REQUEST_ID_CONFLICT".into());
|
||||
}
|
||||
return serde_json::from_str(&receipt_json)
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_RECEIPT_INVALID: {error}"));
|
||||
}
|
||||
|
||||
let authority_id = transaction
|
||||
.query_row(
|
||||
"SELECT value FROM metadata WHERE key = 'authority_id'",
|
||||
[],
|
||||
|row| row.get::<_, String>(0),
|
||||
)
|
||||
.optional()
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_READ_FAILED: {error}"))?
|
||||
.unwrap_or_else(|| format!("local-time-authority-{}", Uuid::new_v4()));
|
||||
transaction
|
||||
.execute(
|
||||
"INSERT OR IGNORE INTO metadata(key, value) VALUES('authority_id', ?1)",
|
||||
params![authority_id],
|
||||
)
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_WRITE_FAILED: {error}"))?;
|
||||
|
||||
let (last_physical_ms, last_logical_counter, last_sequence, previous_ticket_id) = transaction
|
||||
.query_row(
|
||||
"SELECT last_physical_ms, last_logical_counter, last_sequence, last_ticket_id FROM authority_state WHERE singleton = 1",
|
||||
[],
|
||||
|row| Ok((row.get::<_, u64>(0)?, row.get::<_, u64>(1)?, row.get::<_, u64>(2)?, row.get::<_, Option<String>>(3)?)),
|
||||
)
|
||||
.optional()
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_READ_FAILED: {error}"))?
|
||||
.unwrap_or((0, 0, 0, None));
|
||||
|
||||
let clock_rollback_observed = observed_unix_ms < last_physical_ms;
|
||||
let physical_unix_ms = observed_unix_ms.max(last_physical_ms);
|
||||
let logical_counter = if physical_unix_ms > last_physical_ms {
|
||||
0
|
||||
} else {
|
||||
last_logical_counter
|
||||
.checked_add(1)
|
||||
.ok_or("HOLOLAKE_PERSONA_TIME_LOGICAL_COUNTER_EXHAUSTED")?
|
||||
};
|
||||
let issuance_sequence = last_sequence
|
||||
.checked_add(1)
|
||||
.ok_or("HOLOLAKE_PERSONA_TIME_SEQUENCE_EXHAUSTED")?;
|
||||
let logical_timestamp =
|
||||
format!("{physical_unix_ms:013}-{logical_counter:010}-{issuance_sequence:020}");
|
||||
let unique_timestamp = format!("{authority_id}@{logical_timestamp}");
|
||||
let ticket_id = unique_timestamp.clone();
|
||||
let beijing_time = format_beijing_time(physical_unix_ms)?;
|
||||
let elapsed_dates = beijing_day_index(physical_unix_ms) - GUANGHU_EPOCH_BEIJING_DAY_INDEX;
|
||||
let millisecond_chain_origin_ticket_id = transaction
|
||||
.query_row(
|
||||
"SELECT value FROM metadata WHERE key = 'millisecond_chain_origin_ticket_id'",
|
||||
[],
|
||||
|row| row.get::<_, String>(0),
|
||||
)
|
||||
.optional()
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_READ_FAILED: {error}"))?
|
||||
.unwrap_or_else(|| ticket_id.clone());
|
||||
transaction
|
||||
.execute(
|
||||
"INSERT OR IGNORE INTO metadata(key, value) VALUES('millisecond_chain_origin_ticket_id', ?1)",
|
||||
params![millisecond_chain_origin_ticket_id],
|
||||
)
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_WRITE_FAILED: {error}"))?;
|
||||
let mut ticket = PersonaTimeTicket {
|
||||
schema: TICKET_SCHEMA.into(),
|
||||
state: "ISSUED_DURABLE_LOCAL".into(),
|
||||
authority_id,
|
||||
ticket_id: ticket_id.clone(),
|
||||
unique_timestamp,
|
||||
physical_unix_ms,
|
||||
beijing_time,
|
||||
time_zone: "Asia/Shanghai (UTC+08:00)".into(),
|
||||
clock_source: "HOST_OPERATING_SYSTEM_REALTIME_CLOCK".into(),
|
||||
clock_verification: "LOCAL_CLOCK_NOT_NETWORK_ATTESTED".into(),
|
||||
guanghu_epoch_date: GUANGHU_EPOCH_DATE.into(),
|
||||
guanghu_calendar_state: "EPOCH_DATE_LOCKED_EXACT_INSTANT_PENDING".into(),
|
||||
elapsed_beijing_dates_since_guanghu_epoch: elapsed_dates,
|
||||
guanghu_era_day: elapsed_dates + 1,
|
||||
historical_epoch_precision: "DAY_ONLY_EXACT_TIME_UNKNOWN".into(),
|
||||
millisecond_precision_transition_date: MILLISECOND_PRECISION_TRANSITION_DATE.into(),
|
||||
millisecond_chain_origin_ticket_id,
|
||||
logical_counter,
|
||||
issuance_sequence,
|
||||
clock_rollback_observed,
|
||||
human_controller_id: request.human_controller_id,
|
||||
human_controller_verification: "AUTHENTICATED_DIRECT_SESSION".into(),
|
||||
persona_id: request.persona_id,
|
||||
persona_verification: "UNVERIFIED_CALLER_CLAIM".into(),
|
||||
host_software_id: request.host_software_id,
|
||||
host_software_verification: "UNVERIFIED_CALLER_CLAIM".into(),
|
||||
client_instance_id: request.session.client_instance_id,
|
||||
channel_id: request.session.lane_id,
|
||||
session_id: request.session.session_id,
|
||||
previous_ticket_id,
|
||||
request_id: request.request_id,
|
||||
receipt_sha256: String::new(),
|
||||
};
|
||||
ticket.receipt_sha256 = ticket_digest(&ticket);
|
||||
let receipt_json = serde_json::to_string(&ticket)
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_RECEIPT_INVALID: {error}"))?;
|
||||
|
||||
transaction
|
||||
.execute(
|
||||
"INSERT INTO tickets(request_id, request_digest, ticket_id, receipt_json) VALUES(?1, ?2, ?3, ?4)",
|
||||
params![ticket.request_id, request_digest, ticket.ticket_id, receipt_json],
|
||||
)
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_WRITE_FAILED: {error}"))?;
|
||||
transaction
|
||||
.execute(
|
||||
"INSERT INTO authority_state(singleton, last_physical_ms, last_logical_counter, last_sequence, last_ticket_id) VALUES(1, ?1, ?2, ?3, ?4)
|
||||
ON CONFLICT(singleton) DO UPDATE SET last_physical_ms=excluded.last_physical_ms, last_logical_counter=excluded.last_logical_counter, last_sequence=excluded.last_sequence, last_ticket_id=excluded.last_ticket_id",
|
||||
params![physical_unix_ms, logical_counter, issuance_sequence, ticket_id],
|
||||
)
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_WRITE_FAILED: {error}"))?;
|
||||
transaction
|
||||
.commit()
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_COMMIT_FAILED: {error}"))?;
|
||||
Ok(ticket)
|
||||
}
|
||||
|
||||
fn initialize(connection: &Connection) -> Result<(), String> {
|
||||
connection
|
||||
.execute_batch(
|
||||
"PRAGMA synchronous=FULL;
|
||||
CREATE TABLE IF NOT EXISTS metadata(key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS authority_state(
|
||||
singleton INTEGER PRIMARY KEY CHECK(singleton = 1),
|
||||
last_physical_ms INTEGER NOT NULL,
|
||||
last_logical_counter INTEGER NOT NULL,
|
||||
last_sequence INTEGER NOT NULL,
|
||||
last_ticket_id TEXT
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS tickets(
|
||||
request_id TEXT PRIMARY KEY,
|
||||
request_digest TEXT NOT NULL,
|
||||
ticket_id TEXT NOT NULL UNIQUE,
|
||||
receipt_json TEXT NOT NULL
|
||||
);",
|
||||
)
|
||||
.map_err(|error| format!("HOLOLAKE_PERSONA_TIME_STORAGE_UNAVAILABLE: {error}"))
|
||||
}
|
||||
|
||||
fn validate_request(request: &VerifiedTicketRequest) -> Result<(), String> {
|
||||
for (kind, value) in [
|
||||
("REQUEST", request.request_id.as_str()),
|
||||
("HUMAN_CONTROLLER", request.human_controller_id.as_str()),
|
||||
("PERSONA", request.persona_id.as_str()),
|
||||
("HOST_SOFTWARE", request.host_software_id.as_str()),
|
||||
("SESSION", request.session.session_id.as_str()),
|
||||
("CHANNEL", request.session.lane_id.as_str()),
|
||||
(
|
||||
"CLIENT_INSTANCE",
|
||||
request.session.client_instance_id.as_str(),
|
||||
),
|
||||
] {
|
||||
if value.is_empty() || value.len() > MAX_ID_BYTES || value.chars().any(char::is_control) {
|
||||
return Err(format!("HOLOLAKE_PERSONA_TIME_{kind}_ID_INVALID"));
|
||||
}
|
||||
}
|
||||
if request.session.account_key.is_empty() {
|
||||
return Err("HOLOLAKE_PERSONA_TIME_SESSION_CONTEXT_INVALID".into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn request_digest(request: &VerifiedTicketRequest) -> String {
|
||||
sha256_hex(
|
||||
format!(
|
||||
"{}\n{}\n{}\n{}\n{}\n{}\n{}",
|
||||
request.request_id,
|
||||
request.human_controller_id,
|
||||
request.persona_id,
|
||||
request.host_software_id,
|
||||
request.session.account_key,
|
||||
request.session.session_id,
|
||||
request.session.client_instance_id,
|
||||
)
|
||||
.as_bytes(),
|
||||
)
|
||||
}
|
||||
|
||||
fn ticket_digest(ticket: &PersonaTimeTicket) -> String {
|
||||
sha256_hex(
|
||||
format!(
|
||||
"{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
|
||||
ticket.authority_id,
|
||||
ticket.ticket_id,
|
||||
ticket.unique_timestamp,
|
||||
ticket.human_controller_id,
|
||||
ticket.persona_id,
|
||||
ticket.host_software_id,
|
||||
ticket.client_instance_id,
|
||||
ticket.channel_id,
|
||||
ticket.previous_ticket_id.as_deref().unwrap_or(""),
|
||||
)
|
||||
.as_bytes(),
|
||||
)
|
||||
}
|
||||
|
||||
fn sha256_hex(bytes: &[u8]) -> String {
|
||||
digest(&SHA256, bytes)
|
||||
.as_ref()
|
||||
.iter()
|
||||
.map(|byte| format!("{byte:02x}"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn now_unix_ms() -> Result<u64, String> {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|duration| duration.as_millis() as u64)
|
||||
.map_err(|error| format!("HOLOLAKE_SYSTEM_CLOCK_INVALID: {error}"))
|
||||
}
|
||||
|
||||
fn format_beijing_time(unix_ms: u64) -> Result<String, String> {
|
||||
const BEIJING_OFFSET_SECONDS: u64 = 8 * 60 * 60;
|
||||
let unix_seconds = unix_ms / 1000;
|
||||
let shifted = unix_seconds
|
||||
.checked_add(BEIJING_OFFSET_SECONDS)
|
||||
.ok_or("HOLOLAKE_BEIJING_TIME_OUT_OF_RANGE")?;
|
||||
let days = (shifted / 86_400) as i64;
|
||||
let seconds_in_day = shifted % 86_400;
|
||||
let (year, month, day) = civil_from_days(days);
|
||||
let hour = seconds_in_day / 3600;
|
||||
let minute = (seconds_in_day % 3600) / 60;
|
||||
let second = seconds_in_day % 60;
|
||||
let millisecond = unix_ms % 1000;
|
||||
Ok(format!(
|
||||
"{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}.{millisecond:03}+08:00"
|
||||
))
|
||||
}
|
||||
|
||||
fn beijing_day_index(unix_ms: u64) -> i64 {
|
||||
const BEIJING_OFFSET_MS: u64 = 8 * 60 * 60 * 1000;
|
||||
unix_ms
|
||||
.saturating_add(BEIJING_OFFSET_MS)
|
||||
.div_euclid(86_400_000) as i64
|
||||
}
|
||||
|
||||
// Gregorian civil date conversion from Unix epoch days; valid for the positive
|
||||
// Unix timestamps accepted by this runtime and independent of process lifetime.
|
||||
fn civil_from_days(days_since_unix_epoch: i64) -> (i64, u64, u64) {
|
||||
let z = days_since_unix_epoch + 719_468;
|
||||
let era = z.div_euclid(146_097);
|
||||
let day_of_era = z - era * 146_097;
|
||||
let year_of_era =
|
||||
(day_of_era - day_of_era / 1460 + day_of_era / 36_524 - day_of_era / 146_096) / 365;
|
||||
let mut year = year_of_era + era * 400;
|
||||
let day_of_year = day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100);
|
||||
let month_prime = (5 * day_of_year + 2) / 153;
|
||||
let day = day_of_year - (153 * month_prime + 2) / 5 + 1;
|
||||
let month = month_prime + if month_prime < 10 { 3 } else { -9 };
|
||||
year += i64::from(month <= 2);
|
||||
(year, month as u64, day as u64)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn request(id: &str, instance: &str) -> VerifiedTicketRequest {
|
||||
VerifiedTicketRequest {
|
||||
request_id: id.into(),
|
||||
human_controller_id: "ICE-GL∞".into(),
|
||||
persona_id: "ICE-P-ZY001".into(),
|
||||
host_software_id: "HOST-CODEX-MACOS-001".into(),
|
||||
session: AuthenticatedSessionContext {
|
||||
account_key: "verified-account-key".into(),
|
||||
session_id: "channel-session-1".into(),
|
||||
lane_id: "codex-current-channel".into(),
|
||||
client_instance_id: instance.into(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn concurrent_same_millisecond_requests_receive_different_unique_times() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let mut handles = Vec::new();
|
||||
for index in 0..12 {
|
||||
let root = temp.path().to_path_buf();
|
||||
handles.push(std::thread::spawn(move || {
|
||||
issue_at(
|
||||
&root,
|
||||
request(&format!("request-{index}"), &format!("codex-{index}")),
|
||||
1000,
|
||||
)
|
||||
.unwrap()
|
||||
}));
|
||||
}
|
||||
let mut tickets = handles
|
||||
.into_iter()
|
||||
.map(|handle| handle.join().unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
tickets.sort_by_key(|ticket| ticket.issuance_sequence);
|
||||
tickets.dedup_by(|left, right| left.unique_timestamp == right.unique_timestamp);
|
||||
assert_eq!(tickets.len(), 12);
|
||||
assert_eq!(tickets.last().unwrap().issuance_sequence, 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retry_is_idempotent_but_request_id_reuse_with_other_context_is_rejected() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let first = issue_at(temp.path(), request("same-request", "codex-1"), 1000).unwrap();
|
||||
let retry = issue_at(temp.path(), request("same-request", "codex-1"), 2000).unwrap();
|
||||
assert_eq!(first, retry);
|
||||
let error = issue_at(temp.path(), request("same-request", "qoder-1"), 3000).unwrap_err();
|
||||
assert_eq!(error, "HOLOLAKE_PERSONA_TIME_REQUEST_ID_CONFLICT");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn restart_and_clock_rollback_preserve_monotonic_time_and_chain() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let first = issue_at(temp.path(), request("request-1", "codex-1"), 5000).unwrap();
|
||||
let second = issue_at(temp.path(), request("request-2", "codex-2"), 4000).unwrap();
|
||||
assert!(second.clock_rollback_observed);
|
||||
assert_eq!(second.physical_unix_ms, first.physical_unix_ms);
|
||||
assert!(second.unique_timestamp > first.unique_timestamp);
|
||||
assert_eq!(
|
||||
second.previous_ticket_id.as_deref(),
|
||||
Some(first.ticket_id.as_str())
|
||||
);
|
||||
assert_eq!(second.authority_id, first.authority_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reality_time_is_projected_as_beijing_time_even_without_process_uptime() {
|
||||
let coordinate = beijing_time_coordinate(0).unwrap();
|
||||
assert_eq!(coordinate.beijing_time, "1970-01-01T08:00:00.000+08:00");
|
||||
assert!(coordinate.continues_while_hololake_is_closed);
|
||||
assert_eq!(
|
||||
coordinate.clock_verification,
|
||||
"LOCAL_CLOCK_NOT_NETWORK_ATTESTED"
|
||||
);
|
||||
|
||||
let known = beijing_time_coordinate(1_786_947_831_456).unwrap();
|
||||
assert_eq!(known.beijing_time, "2026-08-17T14:23:51.456+08:00");
|
||||
assert_eq!(
|
||||
beijing_time_coordinate(1_745_596_800_000)
|
||||
.unwrap()
|
||||
.elapsed_beijing_dates_since_guanghu_epoch,
|
||||
0
|
||||
);
|
||||
assert_eq!(
|
||||
beijing_time_coordinate(1_745_596_800_000)
|
||||
.unwrap()
|
||||
.guanghu_era_day,
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue