pub struct WanningState { /* private fields */ }Expand description
闸 + 审计日志 + 待支付台账 + 时钟的运行时状态。
Implementations§
Source§impl WanningState
impl WanningState
Sourcepub fn new(clock: SharedClock) -> Self
pub fn new(clock: SharedClock) -> Self
纯内存状态(无审计落盘)。回放与测试用。
Sourcepub fn with_wal(
clock: SharedClock,
wal_path: impl AsRef<Path>,
) -> Result<Self, CoreError>
pub fn with_wal( clock: SharedClock, wal_path: impl AsRef<Path>, ) -> Result<Self, CoreError>
带审计落盘的状态。WAL 打开为追加模式,绝不截断。
Sourcepub fn live(wal_path: impl AsRef<Path>) -> Result<Self, CoreError>
pub fn live(wal_path: impl AsRef<Path>) -> Result<Self, CoreError>
生产状态:系统时钟 + 审计落盘。
注意:不回放已有 WAL——闸从空开始,只往后追加。适合「一次进程一次新账」
的 demo 场景;长期服务重启要接续旧账,用 WanningState::live_resuming。
Sourcepub fn live_resuming(wal_path: impl AsRef<Path>) -> Result<Self, CoreError>
pub fn live_resuming(wal_path: impl AsRef<Path>) -> Result<Self, CoreError>
断点续跑:先整体回放已有 WAL 对账(损坏/篡改/不一致 → fail-closed 拒启), 再换回系统时钟、继续往同一份 WAL 追加。
长期服务(MCP server)重启时用它:账本、撤销、nonce 登记全部从审计接续, 绝不带着一张空账本接着判——否则重启会把 nonce 洗白、把撤销掉的授权复活。
同一份 WAL 同时至多一个活着的写进程(Wal::open 自动持单写者锁):
第二个进程 fail-closed 拒启(CoreError::WalLocked)。两个平台并挂同一份
WAL(.mcp.json + .trae/mcp.json)就是真实场景——并发双闸的内存账本
互不知情,预算硬上限会被合力突破(实测见 tests/single_writer.rs)。
与 WanningState::replay 的区别:replay 冻结在「过去的世界」(注入时钟停在
最后一条记录的 ts、不挂 WAL);本方法校验过后回到「现在的世界」(系统时钟,
继续写审计)。
pub fn gate(&self) -> &Gate
Sourcepub fn pendings(&self) -> &PendingLedger
pub fn pendings(&self) -> &PendingLedger
待支付台账(只读)。AI 侧查询自己 pending 状态只到这一层为止(W-53b: 确认永远不在 AI 工具面上,人在环才不是空转)。
Sourcepub fn pending(&self, pending_id: &str) -> Option<&PendingOrder>
pub fn pending(&self, pending_id: &str) -> Option<&PendingOrder>
按单号查一笔待支付单。
pub fn wal_path(&self) -> Option<&Path>
Sourcepub fn wal_line_count(&self) -> Option<u64>
pub fn wal_line_count(&self) -> Option<u64>
WAL 当前行数;无 WAL 时为 None。审计证据的「WAL 偏移」即行号。
Sourcepub fn last_wal_line(&self) -> Option<u64>
pub fn last_wal_line(&self) -> Option<u64>
最近一次追加的 WAL 行号(1-based);无 WAL 时为 None。
Sourcepub fn audit_chain_tail(&self) -> Option<u64>
pub fn audit_chain_tail(&self) -> Option<u64>
审计完整性链的链尾值(最后一条记录的链值;无 WAL 时为 None)。
对账证据之一:实时侧这个值,与读侧 read_verified
独立重算的链尾必须相等——逐行成链,改历史行而不重算后续整条链,当场现形。
Sourcepub fn register_delegation(
&mut self,
delegation: Delegation,
) -> Result<(), CoreError>
pub fn register_delegation( &mut self, delegation: Delegation, ) -> Result<(), CoreError>
注册委托:先确认必成,再写审计,再入闸(write-ahead)。
Sourcepub fn revoke(&mut self, delegation_id: &str) -> Result<(), CoreError>
pub fn revoke(&mut self, delegation_id: &str) -> Result<(), CoreError>
撤销委托(kill switch):先确认必成,再写审计,再撤销。
Sourcepub fn decide(
&mut self,
intent: &SpendIntent,
) -> Result<GateDecision, CoreError>
pub fn decide( &mut self, intent: &SpendIntent, ) -> Result<GateDecision, CoreError>
判定一笔消费意图:evaluate → 写审计 → commit(write-ahead)。
返回闸的判定。注意失败语义:
- 审计写失败 →
Err,状态零变更(这笔消费没有发生,也不能发生); - 审计写成功但 commit 失败(理论不可达)→
Err,WAL 领先于账本, 回放侧只会更严格,不会放水。
Sourcepub fn decide_opening_pending(
&mut self,
intent: &SpendIntent,
ttl_secs: u64,
) -> Result<(GateDecision, Option<PendingReceipt>), CoreError>
pub fn decide_opening_pending( &mut self, intent: &SpendIntent, ttl_secs: u64, ) -> Result<(GateDecision, Option<PendingReceipt>), CoreError>
人在环待支付(pending_pay 档位,W-53a)的判定入口:
①意图 + ②审批(与 WanningState::decide 同一段)→ ③开待支付单。
ttl_secs == 0或过期时刻溢出 → 在任何落账之前拒绝(API 误用零审计 噪音,W-25 先例;否则会出现「判定已记账却开不出单」的中间世界);- 拒绝 → 正常落 Decide 行,不开单(第二返回值
None); - 放行 → 落 Decide 行、扣预算、落 Pending 行、台账开单,返回
PendingReceipt(单号 + 审批额 + 过期时刻 + 待支付行号)。
预算在开单时扣(与闸「放行即记账」同一语义):等人确认期间这笔额度 已被占用,并发多单不可能合力突破硬上限;确认不二次扣,过期作废不退 (作废是账本事实,退了才给「反复开单洗预算」留门)。
Sourcepub fn confirm_pending(
&mut self,
pending_id: &str,
amount_cents: u64,
proof: &str,
) -> Result<PendingOrder, CoreError>
pub fn confirm_pending( &mut self, pending_id: &str, amount_cents: u64, proof: &str, ) -> Result<PendingOrder, CoreError>
④人确认(wanning confirm 人工面;不在 AI 工具面上,W-53b)。
三钉在 PendingLedger::check_confirm:金额一致 → 幂等 → TTL。被拒的
确认一行都不落;唯一的例外是过期确认——作废本身是账本事实,先落一行
Terminal{ExpiredVoid} 把单作废,再拒(第二次确认就是普通的幂等拒)。
成功 = 落 Confirm 行 + 落 Terminal{Completed} 行,返回完成态的单。
Sourcepub fn void_expired_pendings(&mut self) -> Result<Vec<String>, CoreError>
pub fn void_expired_pendings(&mut self) -> Result<Vec<String>, CoreError>
批量物化 TTL 过期:扫出台账里所有已过期且仍 Open 的单,逐张落
Terminal{ExpiredVoid} 行并作废,返回作废的单号(按单号有序)。
幂等:已作废/已确认/已完成的单不在扫描范围,再扫一遍返回空。审计展示或 定时任务用它把「过期作废」从隐式(确认时才撞上)变成显式账本事实。
Sourcepub fn state_hash(&self) -> u64
pub fn state_hash(&self) -> u64
闸状态指纹(FNV-1a 64,非密码学,仅用于确定性对账)。
覆盖:委托集、账本、撤销集、nonce 登记集、策略运行时状态(W-27 速率 窗口时刻与类目台账——随 commit 演化的状态必须进指纹,否则「速率窗口跨 重启被洗掉」这类回放缺失对账不出来)、待支付台账(W-53a:单的状态演化 必须进指纹,否则「重启洗掉确认」对账不出来);全部按有序迭代序列化, 因此「同一份 WAL 回放两遍 hash 必相同」由构造保证。
pendings 键只在台账非空时出现——老账本(无人待支付)的指纹与 W-53
之前逐字节相同,不制造一次全体哈希漂移。