# 双端交换与累计预算

完整可运行示例：`moon run examples/exchange`。示例使用两个字节队列替代传输通道，真实编码并解码 MRCL 帧，验证首次成功、扩容成功、字节耗尽三种结果。将队列替换为自己的 HTTP/WebSocket/文件适配器即可，核心无需依赖该通道。

## 驱动顺序

1. 冻结双方 ID 清单，确定 scope、各自 epoch 与共同 seed；从同一份冻结清单生成各轮 Snapshot。
2. 用初始源 Snapshot、目标 epoch 和 SessionLimits 创建 Session。
3. 发送本地编码帧前调用 record_sent。只有仍返回 Awaiting 时才发送；Stopped 意味着预算已拒绝该次发送，sent_bytes 不增加。实际传输失败可能仍发生字节消耗，已授权的发送按完整帧记账；重传必须再次调用。
4. 每收到一个远端帧调用 receive，并提供本轮配置对应的源 Snapshot。实际收到的字节先记账，包括重复、损坏或超限帧；已停止的会话拒绝后续输入。
5. Awaiting(next) 表示双方用 next 配置重建摘要。Complete(plan) 才能请求内容并应用计划；Stopped(reason) 交给业务决定是否交换全量清单或结束。

这个账本依赖适配器如实报告 IO。绕过 record_sent 的发送无法被库自动获知；网络头、对象下载和回退全量传输不在 MRCL 帧预算内。超过预算的接收帧已经到达，计量可以大于上限；库立即停止后续解码和协商。若需要在传输层提前限制读取，应同时限制响应体长度/接收缓冲。

## 限额与状态

SessionLimits 默认：最多 8 轮、累计收发 8 MiB、累计 100000 次 peeling、width≤65536、成员数≤10000000。可配置上限：64 轮、1 TiB、1000000 次 peeling、width≤262144。非法值在创建时拒绝。步数不是所有计算成本，表宽还约束扫描与分配。

| 停止原因 | 意义与建议 |
|---|---|
| byte_limit | 下一次发送不在预算内，或已收到字节超限；停止协商 |
| round_limit | 已用尽允许解码的轮数；由应用评估全量回退 |
| work_limit | 累计 peeling 次数耗尽，不发布部分计划 |
| width_limit | 不能再扩大摘要；由应用选择其他方案 |
| snapshot_changed | scope、任一方 epoch 或冻结集合指纹改变；废弃旧会话并重新冻结 |
| protocol_error | 配置不符、坏帧、核验失败等；返回具体异常且会话失效，不作为普通容量不足重试 |

首次有效目标帧固定目标指纹。后续同 epoch 但指纹改变也拒绝，避免写入方错误复用版本标签。相同已接受帧的精确重传仍消耗字节，但不增加轮数或恢复步数，也不重复生成/应用变更；完成后的精确重传可被识别，新帧则报错。存储已接受帧用于精确比较，内存占用受累计字节和轮数预算约束；不只凭 CRC 判断重复。

本地会话不在 MRCL v1 中新增认证会话 ID。远程多路复用、会话路由和防重放凭证由适配器绑定，scope/epoch/seed 与非密码学指纹不是认证协议。停止是终态，需新建 Session 才能继续。

示例固定结果：首次成功 1 轮、双向共 2056 字节；从 width=1 重试后 3 轮、双向共 1248 字节；预算 256 字节时，在授权下一次发送前停止，累计已发送/接收各128字节。这些数字仅属于示例输入，不是普遍性能保证。
