refactor(runtime): 收据写者归一为 Agent wrapper

RuntimeFileStore.writeRuntimeReceipt 生产零调用(17 处调用全为测试夹具),
从生产 interface 删除;测试侧经 RuntimeReceiptFixture 的 extension 以同签名
承接,调用点零改动。收据格式知识仍由 RuntimeDeploymentReceipt 类型承载,
GUI 侧只保留 readRuntimeReceipt 读缝。

评审候选⑥ 6a:wrapper 是唯一收据写者,GUI 永不伪造收据。
This commit is contained in:
Qiu Yuzhou
2026-10-04 12:59:26 +08:00
parent d9460c4d6d
commit 945ea2b4db
2 changed files with 25 additions and 14 deletions
+2 -14
View File
@@ -156,25 +156,13 @@ struct RuntimeFileStore {
try? Data(contentsOf: fileURL)
}
/// 部署收据只读:唯一生产写者是 Agent wrapper(AgentContract.writeRuntimeReceipt),
/// GUI 在健康检查中核对 pid 与契约摘要,从不写。
func readRuntimeReceipt() -> RuntimeDeploymentReceipt? {
guard let data = try? Data(contentsOf: runtimeStatusFileURL) else { return nil }
return try? JSONDecoder().decode(RuntimeDeploymentReceipt.self, from: data)
}
func writeRuntimeReceipt(for document: SslocalRuntimeDocument, processID: Int32) throws {
guard let digest = document.deploymentSHA256 else {
throw PersistenceError.ioFailure(detail: "Runtime contract digest is unavailable")
}
let receipt = RuntimeDeploymentReceipt(processID: processID, contractSHA256: digest)
let encoder = JSONEncoder()
encoder.outputFormatting = [.sortedKeys]
do {
try AtomicFileWriter.write(try encoder.encode(receipt), to: runtimeStatusFileURL)
} catch {
throw PersistenceError.ioFailure(detail: String(describing: error))
}
}
/// 显式停止清理(D2 停止协议末端):运行时契约、wrapper pid、ACL 链接与
/// 变体、digest 清单及原子写残留。异常崩溃路径不调用本方法(文件保留供
/// KeepAlive 重放,D5)。文件缺失时调用无害。
@@ -0,0 +1,23 @@
import Foundation
@testable import ShadowsocksX_NG2
/// 测试支持:模拟 wrapper 写入部署收据。生产唯一收据写者是 Agent wrapper
/// (AgentContract.writeRuntimeReceipt),GUI 侧只有读(readRuntimeReceipt);
/// 收据格式知识由 Domain 的 RuntimeDeploymentReceipt 类型承载,此处只复刻
/// wrapper 的「写动作」供夹具布置磁盘状态。
extension RuntimeFileStore {
func writeRuntimeReceipt(for document: SslocalRuntimeDocument, processID: Int32) throws {
guard let digest = document.deploymentSHA256 else {
throw PersistenceError.ioFailure(detail: "Runtime contract digest is unavailable")
}
let receipt = RuntimeDeploymentReceipt(processID: processID, contractSHA256: digest)
let encoder = JSONEncoder()
encoder.outputFormatting = [.sortedKeys]
do {
try AtomicFileWriter.write(try encoder.encode(receipt), to: runtimeStatusFileURL)
} catch {
throw PersistenceError.ioFailure(detail: String(describing: error))
}
}
}