hololake-system-architecture/product-source/hololake-native-desktop/src-tauri/src/channel_receipt.rs

640 lines
21 KiB
Rust

//! Native adapter for GH-CHANNEL-RECEIPT-0001.
//! Language, authorization, execution, verification and reception remain
//! separate. A model response can never become reality evidence by itself.
use ring::digest::{digest, SHA256};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::collections::{BTreeSet, HashSet};
use std::fs;
use std::io::Write;
use std::path::PathBuf;
use tauri::AppHandle;
const SCHEMA: &str = "guanghu.channel-receipt/v1";
const CONTRACT_ID: &str = "GH-CHANNEL-RECEIPT-0001";
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ChannelEvidence {
pub r#type: String,
pub r#ref: String,
#[serde(default)]
pub verified: bool,
#[serde(default)]
pub external: bool,
#[serde(default)]
pub proves: Vec<String>,
#[serde(default)]
pub does_not_prove: Vec<String>,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ChannelAuthorization {
pub current: bool,
pub scoped: bool,
pub unexpired: bool,
pub r#ref: String,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ChannelParallelStates {
pub session: String,
pub broadcast: String,
pub handoff: String,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ChannelHandoffAcceptance {
pub target_acceptance: bool,
pub target_id: String,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct CompileChannelReceiptInput {
pub event_id: String,
pub channel_id: String,
pub channel_mode: String,
pub source_layer: String,
pub actor_id: String,
pub intent: String,
pub claim_layer: String,
#[serde(default)]
pub required_proof: Vec<String>,
#[serde(default)]
pub evidence: Vec<ChannelEvidence>,
pub authorization: Option<ChannelAuthorization>,
pub current_states: Option<ChannelParallelStates>,
pub prior_receipt_id: Option<String>,
pub handoff: Option<ChannelHandoffAcceptance>,
}
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ChannelReceipt {
pub schema: &'static str,
pub contract_id: &'static str,
pub receipt_id: String,
pub event_id: String,
pub received: bool,
pub identified: Option<String>,
pub channel: Value,
pub intent: String,
pub claim: Value,
pub affected_layer: String,
pub effect: String,
pub reality_status: String,
pub evidence: Vec<ChannelEvidence>,
pub required_proof: Vec<String>,
pub states: Value,
pub supersedes: Vec<String>,
pub boundaries: Vec<String>,
pub next_step: String,
pub previous_ledger_hash: String,
pub ledger_receipt_hash: String,
}
pub fn compile(
app: AppHandle,
input: CompileChannelReceiptInput,
) -> Result<ChannelReceipt, String> {
let mut receipt = compile_pure(input)?;
let path = ledger_path(&app)?;
let previous = last_hash(&path)?;
receipt.previous_ledger_hash = previous.clone();
let material = serde_json::to_vec(&json!({
"receiptId": receipt.receipt_id,
"eventId": receipt.event_id,
"effect": receipt.effect,
"realityStatus": receipt.reality_status,
"boundaries": receipt.boundaries,
"previousLedgerHash": previous
}))
.map_err(|error| format!("HOLOLAKE_CHANNEL_RECEIPT_INVALID: {error}"))?;
receipt.ledger_receipt_hash = sha256(&material);
append(&path, &receipt)?;
Ok(receipt)
}
fn compile_pure(input: CompileChannelReceiptInput) -> Result<ChannelReceipt, String> {
for (label, value) in [
("eventId", input.event_id.as_str()),
("channelId", input.channel_id.as_str()),
("actorId", input.actor_id.as_str()),
] {
if value.trim().is_empty() || value.chars().count() > 512 {
return Err(format!("HOLOLAKE_CHANNEL_RECEIPT_{label}_INVALID"));
}
}
ensure_member(
&input.source_layer,
&[
"HUMAN_DIRECT_LANGUAGE",
"MODEL_RESPONSE",
"LATER_CORRECTION",
"REALITY_EVIDENCE",
],
"SOURCE_LAYER",
)?;
ensure_member(
&input.claim_layer,
&[
"LANGUAGE_ONTOLOGY",
"LANGUAGE_SIMULATION",
"ENGINEERING_PROPOSAL",
"REALITY_CLAIM",
],
"CLAIM_LAYER",
)?;
ensure_member(
&input.channel_mode,
&[
"CONVERSATION_ONLY",
"LANGUAGE_ARCHITECTURE",
"REALITY_EXECUTION",
],
"CHANNEL_MODE",
)?;
ensure_member(
&input.intent,
&[
"IDENTIFY",
"ENTER_CHANNEL",
"CONVERSE",
"DECLARE",
"REQUEST_PLAN",
"REQUEST_BROADCAST",
"ACTIVATE_LOCAL_MODE",
"DRAFT_BROADCAST",
"RECEIVE_DOCUMENT",
"PUBLISH_EXTERNAL",
"QUERY_STATUS",
"PROPOSE_HANDOFF",
"ACCEPT_HANDOFF",
"REQUEST_REALITY_ACTION",
"REPORT_RESULT",
"CORRECT",
"END_INTERACTION",
"EXIT_CHANNEL",
"SEAL_LANGUAGE_RECORD",
"ORDINARY_DIALOGUE",
],
"INTENT",
)?;
for evidence in &input.evidence {
if evidence.r#type.trim().is_empty() || evidence.r#ref.trim().is_empty() {
return Err("HOLOLAKE_CHANNEL_RECEIPT_EVIDENCE_INVALID".into());
}
}
if input
.required_proof
.iter()
.any(|proof| proof.trim().is_empty())
{
return Err("HOLOLAKE_CHANNEL_RECEIPT_REQUIRED_PROOF_INVALID".into());
}
let required_proof = input
.required_proof
.iter()
.cloned()
.collect::<BTreeSet<_>>()
.into_iter()
.collect::<Vec<_>>();
let declared = input
.evidence
.iter()
.filter(|item| item.r#type == "EXECUTION_RECEIPT" && item.external && item.verified)
.flat_map(|item| item.proves.iter().cloned())
.collect::<HashSet<_>>();
let has_external_receipt = input
.evidence
.iter()
.any(|item| item.r#type == "EXECUTION_RECEIPT" && item.external && item.verified)
&& required_proof.iter().all(|proof| declared.contains(proof));
let authorized = input.authorization.as_ref().is_some_and(|authorization| {
authorization.current
&& authorization.scoped
&& authorization.unexpired
&& !authorization.r#ref.trim().is_empty()
});
let before = input.current_states.as_ref().map_or_else(
|| json!({"session":"DISCONNECTED","broadcast":"CLOSED","handoff":"NONE"}),
|states| json!({"session":states.session,"broadcast":states.broadcast,"handoff":states.handoff}),
);
ensure_member(
before["session"].as_str().unwrap_or(""),
&[
"DISCONNECTED",
"IDENTIFIED",
"CHANNEL_ACTIVE",
"STANDBY",
"CLOSED",
],
"SESSION_STATE",
)?;
ensure_member(
before["broadcast"].as_str().unwrap_or(""),
&[
"CLOSED",
"ARMED",
"DRAFTED",
"HUMAN_CONFIRMED",
"RECEIVED_LOCAL",
"EXTERNAL_IN_PROGRESS",
"EXTERNAL_VERIFIED",
],
"BROADCAST_STATE",
)?;
ensure_member(
before["handoff"].as_str().unwrap_or(""),
&["NONE", "PROPOSED", "TARGET_ACCEPTED", "ACTIVE", "REVOKED"],
"HANDOFF_STATE",
)?;
let mut after = before.clone();
let mut correction = "CLEAN";
let mut effect = "LANGUAGE_STATE_ONLY";
let mut reality_status = "NOT_APPLICABLE";
let mut next_step = "CONTINUE_IN_CHANNEL";
let mut boundaries = BTreeSet::new();
let reality_claim = input.claim_layer == "REALITY_CLAIM";
let simulation = input.claim_layer == "LANGUAGE_SIMULATION";
let conversation_only = input.channel_mode == "CONVERSATION_ONLY";
if matches!(
input.intent.as_str(),
"IDENTIFY"
| "ENTER_CHANNEL"
| "CONVERSE"
| "ORDINARY_DIALOGUE"
| "END_INTERACTION"
| "EXIT_CHANNEL"
) {
effect = "NO_EXTERNAL_EFFECT";
}
if input.intent == "IDENTIFY" {
after["session"] = json!("IDENTIFIED");
}
if input.intent == "ENTER_CHANNEL" {
after["session"] = json!("CHANNEL_ACTIVE");
}
if matches!(input.intent.as_str(), "END_INTERACTION" | "EXIT_CHANNEL") {
after["session"] = json!("CLOSED");
next_step = "STOP_OUTPUT_AFTER_MINIMAL_CLOSE_RECEIPT";
boundaries.insert("NO_ROLEPLAY_EPILOGUE_AFTER_CLOSE");
}
if before["session"] == "CLOSED"
&& !matches!(input.intent.as_str(), "IDENTIFY" | "ENTER_CHANNEL")
{
effect = "NO_EXTERNAL_EFFECT";
reality_status = "REJECTED";
next_step = "REQUIRE_NEW_HUMAN_DIRECT_CHANNEL_ENTRY";
boundaries.insert("CLOSED_SESSION_CANNOT_REACTIVATE_ITSELF");
}
if input.intent == "REQUEST_PLAN" || input.claim_layer == "ENGINEERING_PROPOSAL" {
effect = "PLAN_ONLY";
reality_status = "PROPOSED";
next_step = "REVIEW_PLAN";
}
match input.intent.as_str() {
"ACTIVATE_LOCAL_MODE" => {
after["broadcast"] = json!("ARMED");
effect = "LANGUAGE_STATE_ONLY";
next_step = "WAIT_FOR_DOCUMENT_OR_DRAFT_REQUEST";
}
"DRAFT_BROADCAST" => {
after["broadcast"] = json!("DRAFTED");
effect = "PLAN_ONLY";
next_step = "HUMAN_REVIEW_DRAFT";
}
"RECEIVE_DOCUMENT" => {
after["broadcast"] = json!("RECEIVED_LOCAL");
effect = "LANGUAGE_STATE_ONLY";
next_step = "RECORD_LOCAL_RECEIPT_ONLY";
boundaries.insert("RECEIVED_LOCAL_IS_NOT_EXTERNAL_PUBLISH");
}
_ => {}
}
if simulation {
effect = "LANGUAGE_STATE_ONLY";
reality_status = "NOT_APPLICABLE";
boundaries.insert("LANGUAGE_SIMULATION_ONLY");
}
if reality_claim {
reality_status = if has_external_receipt {
"VERIFIED"
} else {
"UNVERIFIED"
};
effect = if has_external_receipt {
"RESULT_RECORDED"
} else {
"NO_EXTERNAL_EFFECT"
};
next_step = if has_external_receipt {
"READ_BACK_EXTERNAL_RESULT"
} else {
"PROVIDE_EXTERNAL_EVIDENCE"
};
}
if input.intent == "REQUEST_REALITY_ACTION" {
if conversation_only {
effect = "NO_EXTERNAL_EFFECT";
reality_status = "REJECTED";
next_step = "MOVE_TO_AUTHORIZED_EXECUTION_CHANNEL";
boundaries.insert("CONVERSATION_CHANNEL_CANNOT_EXECUTE");
} else if !authorized {
effect = "PENDING_AUTHORIZATION";
reality_status = "PENDING_AUTHORIZATION";
next_step = "OBTAIN_CURRENT_SCOPED_AUTHORIZATION";
} else {
effect = "EXECUTION_ELIGIBLE";
reality_status = if has_external_receipt {
"VERIFIED"
} else {
"IN_PROGRESS_WITH_RECEIPT"
};
next_step = if has_external_receipt {
"READ_BACK_EXTERNAL_RESULT"
} else {
"EXECUTE_THROUGH_AUTHORIZED_ADAPTER"
};
}
}
if input.intent == "PUBLISH_EXTERNAL" {
if !authorized {
effect = "PENDING_AUTHORIZATION";
reality_status = "PENDING_AUTHORIZATION";
next_step = "OBTAIN_CURRENT_SCOPED_PUBLISH_AUTHORIZATION";
} else if has_external_receipt {
effect = "RESULT_RECORDED";
reality_status = "VERIFIED";
after["broadcast"] = json!("EXTERNAL_VERIFIED");
next_step = "READ_BACK_EXTERNAL_PUBLICATION";
} else {
effect = "EXECUTION_ELIGIBLE";
reality_status = "IN_PROGRESS_WITH_RECEIPT";
after["broadcast"] = json!("EXTERNAL_IN_PROGRESS");
next_step = "PUBLISH_THROUGH_AUTHORIZED_ADAPTER_AND_CAPTURE_READBACK";
}
}
if input.intent == "REQUEST_BROADCAST" {
if conversation_only || !authorized {
effect = "LANGUAGE_STATE_ONLY";
reality_status = if reality_claim {
"UNVERIFIED"
} else {
"NOT_APPLICABLE"
};
next_step = "OBTAIN_EXPLICIT_BROADCAST_AUTHORIZATION";
boundaries.insert("DRAFT_OR_LANGUAGE_BROADCAST_ONLY");
} else {
effect = "EXECUTION_ELIGIBLE";
reality_status = if has_external_receipt {
"VERIFIED"
} else {
"IN_PROGRESS_WITH_RECEIPT"
};
next_step = if has_external_receipt {
"READ_BACK_EXTERNAL_RESULT"
} else {
"SEND_THROUGH_AUTHORIZED_BROADCAST_ADAPTER"
};
}
}
if input.intent == "CORRECT" {
effect = "LANGUAGE_STATE_ONLY";
reality_status = "NOT_APPLICABLE";
next_step = "CREATE_CORRECTION_CANDIDATE_AND_RETEST";
correction = if input.prior_receipt_id.is_some() {
"SUPERSEDED"
} else {
"CORRECTION_PENDING"
};
}
if input.intent == "QUERY_STATUS" {
effect = if has_external_receipt {
"RESULT_RECORDED"
} else {
"NO_EXTERNAL_EFFECT"
};
reality_status = if has_external_receipt {
"VERIFIED"
} else {
"UNVERIFIED"
};
next_step = if has_external_receipt {
"REPORT_OBSERVED_STATUS_WITH_SCOPE"
} else {
"REPORT_NO_OBSERVATION_SOURCE"
};
if !has_external_receipt {
boundaries.insert("DO_NOT_GUESS_STATUS_WITHOUT_OBSERVATION");
}
}
if input.intent == "PROPOSE_HANDOFF" {
after["handoff"] = json!("PROPOSED");
effect = "LANGUAGE_STATE_ONLY";
reality_status = "PROPOSED";
next_step = "WAIT_FOR_INDEPENDENT_TARGET_ACCEPTANCE";
boundaries.insert("PROPOSAL_IS_NOT_ACTIVE_HANDOFF");
}
if input.intent == "ACCEPT_HANDOFF" {
let accepted = input.handoff.as_ref().is_some_and(|handoff| {
handoff.target_acceptance && handoff.target_id == input.actor_id
});
if before["handoff"] != "PROPOSED" || !accepted {
effect = "NO_EXTERNAL_EFFECT";
reality_status = "REJECTED";
next_step = "OBTAIN_INDEPENDENT_ACCEPTANCE_FROM_TARGET";
boundaries.insert("MODEL_CANNOT_ACCEPT_FOR_TARGET");
} else {
after["handoff"] = json!("TARGET_ACCEPTED");
effect = "LANGUAGE_STATE_ONLY";
reality_status = "PROPOSED";
next_step = "APPLY_SCOPED_HANDOFF_AND_CAPTURE_READBACK";
}
}
if input.source_layer == "MODEL_RESPONSE" {
boundaries.insert("MODEL_RESPONSE_IS_NOT_EXTERNAL_EVIDENCE");
}
if input.intent == "IDENTIFY" {
boundaries.insert("IDENTIFICATION_IS_NOT_BROADCAST");
}
if input.intent == "ENTER_CHANNEL" {
boundaries.insert("CHANNEL_ENTRY_IS_NOT_PERSONA_OR_AUTHORITY_TRANSFER");
}
if !required_proof.is_empty() && !has_external_receipt {
boundaries.insert("EVIDENCE_SCOPE_DOES_NOT_PROVE_REQUESTED_EFFECT");
}
if !has_external_receipt && (reality_status == "VERIFIED" || effect == "RESULT_RECORDED") {
return Err("HOLOLAKE_CHANNEL_RECEIPT_EXTERNAL_EVIDENCE_REQUIRED".into());
}
let boundaries = boundaries
.into_iter()
.map(str::to_string)
.collect::<Vec<_>>();
let supersedes = if correction == "SUPERSEDED" {
input.prior_receipt_id.clone().into_iter().collect()
} else {
Vec::new()
};
let base = json!({
"schema": SCHEMA, "contractId": CONTRACT_ID, "eventId": input.event_id,
"received": true, "identified": if input.intent == "IDENTIFY" { Some(input.actor_id.clone()) } else { None },
"channel": {"id":input.channel_id,"mode":input.channel_mode}, "intent":input.intent,
"claim":{"layer":input.claim_layer,"sourceLayer":input.source_layer},
"affectedLayer":if reality_claim { "REALITY_EVIDENCE_LAYER" } else { "LANGUAGE_LAYER" },
"effect":effect,"realityStatus":reality_status,"evidence":input.evidence,
"requiredProof":required_proof,"states":{"before":before,"after":after,"correction":correction},
"supersedes":supersedes,"boundaries":boundaries,"nextStep":next_step
});
let receipt_id = format!(
"GH-CR-{}",
&sha256(&serde_json::to_vec(&base).unwrap_or_default())[..20]
);
Ok(ChannelReceipt {
schema: SCHEMA,
contract_id: CONTRACT_ID,
receipt_id,
event_id: base["eventId"].as_str().unwrap_or_default().into(),
received: true,
identified: base["identified"].as_str().map(str::to_string),
channel: base["channel"].clone(),
intent: base["intent"].as_str().unwrap_or_default().into(),
claim: base["claim"].clone(),
affected_layer: base["affectedLayer"].as_str().unwrap_or_default().into(),
effect: effect.into(),
reality_status: reality_status.into(),
evidence: input.evidence,
required_proof,
states: base["states"].clone(),
supersedes,
boundaries,
next_step: next_step.into(),
previous_ledger_hash: "GENESIS".into(),
ledger_receipt_hash: String::new(),
})
}
fn ensure_member(value: &str, allowed: &[&str], label: &str) -> Result<(), String> {
if allowed.contains(&value) {
Ok(())
} else {
Err(format!("HOLOLAKE_CHANNEL_RECEIPT_{label}_UNSUPPORTED"))
}
}
fn ledger_path(app: &AppHandle) -> Result<PathBuf, String> {
Ok(
crate::authenticated_storage::account_storage_root(app, "channel-receipts-v1")?
.join("receipts.jsonl"),
)
}
fn last_hash(path: &PathBuf) -> Result<String, String> {
if !path.exists() {
return Ok("GENESIS".into());
}
let body = fs::read_to_string(path)
.map_err(|error| format!("HOLOLAKE_CHANNEL_RECEIPT_LEDGER_READ_FAILED: {error}"))?;
Ok(body
.lines()
.last()
.and_then(|line| serde_json::from_str::<Value>(line).ok())
.and_then(|value| {
value
.get("ledgerReceiptHash")
.and_then(Value::as_str)
.map(str::to_string)
})
.unwrap_or_else(|| "GENESIS".into()))
}
fn append(path: &PathBuf, receipt: &ChannelReceipt) -> Result<(), String> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
.map_err(|error| format!("HOLOLAKE_CHANNEL_RECEIPT_LEDGER_WRITE_FAILED: {error}"))?;
}
let mut file = fs::OpenOptions::new()
.create(true)
.append(true)
.open(path)
.map_err(|error| format!("HOLOLAKE_CHANNEL_RECEIPT_LEDGER_WRITE_FAILED: {error}"))?;
writeln!(
file,
"{}",
serde_json::to_string(receipt)
.map_err(|error| format!("HOLOLAKE_CHANNEL_RECEIPT_INVALID: {error}"))?
)
.map_err(|error| format!("HOLOLAKE_CHANNEL_RECEIPT_LEDGER_WRITE_FAILED: {error}"))
}
fn sha256(bytes: &[u8]) -> String {
digest(&SHA256, bytes)
.as_ref()
.iter()
.map(|byte| format!("{byte:02x}"))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
fn input(intent: &str, claim_layer: &str) -> CompileChannelReceiptInput {
CompileChannelReceiptInput {
event_id: "E-1".into(),
channel_id: "ICE-CH-ZC001".into(),
channel_mode: "REALITY_EXECUTION".into(),
source_layer: "MODEL_RESPONSE".into(),
actor_id: "carrier".into(),
intent: intent.into(),
claim_layer: claim_layer.into(),
required_proof: vec![],
evidence: vec![],
authorization: None,
current_states: None,
prior_receipt_id: None,
handoff: None,
}
}
#[test]
fn model_reality_claim_stays_unverified_without_external_receipt() {
let receipt = compile_pure(input("REPORT_RESULT", "REALITY_CLAIM")).unwrap();
assert_eq!(receipt.reality_status, "UNVERIFIED");
assert_eq!(receipt.effect, "NO_EXTERNAL_EFFECT");
assert!(receipt
.boundaries
.contains(&"MODEL_RESPONSE_IS_NOT_EXTERNAL_EVIDENCE".into()));
}
#[test]
fn reality_action_requires_current_scoped_authorization() {
let receipt = compile_pure(input("REQUEST_REALITY_ACTION", "REALITY_CLAIM")).unwrap();
assert_eq!(receipt.reality_status, "PENDING_AUTHORIZATION");
assert_eq!(receipt.effect, "PENDING_AUTHORIZATION");
}
#[test]
fn verified_status_requires_proof_scoped_external_receipt() {
let mut value = input("QUERY_STATUS", "REALITY_CLAIM");
value.required_proof = vec!["target-a".into()];
value.evidence.push(ChannelEvidence {
r#type: "EXECUTION_RECEIPT".into(),
r#ref: "receipt://1".into(),
verified: true,
external: true,
proves: vec!["target-a".into()],
does_not_prove: vec!["target-b".into()],
});
let receipt = compile_pure(value).unwrap();
assert_eq!(receipt.reality_status, "VERIFIED");
assert_eq!(receipt.effect, "RESULT_RECORDED");
}
}