Files
mtgodot-poc/audit/contracts/social.exchange-safebox.md
T
shenleiandClaude Opus 5 a989b8f19b chore: delete 106 unreachable *_system.gd and their parity tests
These files were loaded only by their own test_*_parity.gd and never by
the runtime (transitively checked, incl. client_phase_integration_system).
Removes 76 manifest implementation/evidence references across 18
contracts; contract docs get a note that conclusions based on them are
void. Live systems kept: chest, consumable, item_attr, metin_socket,
whisper_chat.

Full headless suite: no new failures (7 failing + 5 headless-hanging
render tests are identical before the deletion).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 17:51:52 +09:00

104 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# social.exchange_safebox
## Scope
审计玩家面对面交易、交易确认/防调包、仓库(Safebox)、仓库密码与分页、商城(Mall)只读取出、金币变化和窗口距离/关闭清理。判断标准是当前端是否复现 40250 的客户端请求门控、服务器回包权威状态、物品 socket/属性传递、错误分支和窗口时序;本地脚本能够独立完成一次交易或仓库存取,不能作为与 40250 实现统一的证明。
## Reference call chain
- `UserInterface/PythonNetworkStreamPhaseGame.cpp`
- `RecvExchangePacket` 按 `START/ITEM_ADD/ITEM_DEL/ELK_ADD/ACCEPT/END` 更新 `CPythonExchange`,每个物品同时保存 socket 与 attribute,并在物品变化后刷新交易和背包窗口。
- `SendExchangeStartPacket`、`SendExchangeElkAddPacket`、`SendExchangeItemAddPacket`、`SendExchangeAcceptPacket`、`SendExchangeExitPacket` 均先经过 `__CanActMainInstance()`,再发送 `CG_EXCHANGE` 并追加 sequence;客户端不直接结算交易。
- `UserInterface/PythonExchange.cpp` / `Client/Eternexus/root/uiexchange.py`
- `CPythonExchange` 保存双方姓名、12 个展示槽、socket/attribute、金币和接受状态;交易结束由 `GC_END` 驱动 `EndExchange`,成功/失败详情通过服务器聊天提示提供。
- 交易 UI 只提交物品、金币、接受或退出请求,等待服务器回包刷新;物品源位置、anti flag、背包和金币结果由服务端确认。
- `UserInterface/PythonNetworkStreamPhaseGameItem.cpp`
- Safebox checkin/checkout/move、密码错误、大小、金币变化和 Mall open/set/del/checkout 都是协议路径;Safebox/Mall item set 回包复制 vnum/count/flags/anti_flags、socket 和 attribute 后刷新窗口。
- Safebox 金币主动修改包在该版本明确未启用;Mall checkout 还触发对应掉落音效。
- `Client/Eternexus/root/uisafebox.py` / `game.py`
- 仓库由服务器返回页数和物品状态,5×9 每页 45 格;拖放/右键发送 checkin、checkout 或 move,关闭和密码命令走服务器流程。商城只允许取出,不允许向 Mall 存入。
## Current call chain
- native `M2Client`/`ClassicSession` 已提供 exchange start/item/gold/accept/cancel、safebox checkin/checkout/move/password/change/close、mall checkout/password,以及 `EntityStore` 的 exchange/safebox/mall parser、dirty 标记和 signal。
- `EntityStore` 能保存交易双方 12 槽、金币、接受标志、notice,能够复制交易物品 socket/attribute;Safebox/Mall parser 能复制 item flags、anti_flags、socket 和 attribute,并处理 open/set/del/close/wrong-password 基础事件。
- `project/ui/exchange_ui.gd`、`safebox_ui.gd`、`mall_ui.gd` 已提供 12 槽交易 UI、45 格分页仓库、商城只读出、tooltip、密码和距离自动关闭;主要 UI 已从 native getter/signal 刷新,而不是只依赖本地物品列表。
- `project/player_trade_exchange_system.gd` 和 `project/safebox_storage_keeper_system.gd` 是另外一套本地状态机:它们直接保存玩家字典、交易物品、金币、仓库密码、库存和金条熔铸结果。这些 helper 可用于单元测试,但不是 40250 客户端的网络 authority 链。
## Branch matrix
| 场景 | 40250 | 当前端 | 结论 |
|---|---|---|---|
| 开始交易 | `__CanActMainInstance`→`CG_EXCHANGE START`→服务器 `GC_START` | native 能发 start,UI/helper 能打开交易 | PARTIAL:native 缺同等动作门控和 active/partner 状态限制;helper 可绕开服务器 |
| 放入物品 | 客户端只发源位置和展示槽,服务器回 `GC_ITEM_ADD`;变化时双方接受状态由服务器清除 | UI 做 anti-give/重复源位置检查后立即记录 `_offered_cells`,native 只做位置边界 | PARTIAL:本地 optimistic 集合没有按 `GC_ITEM_ADD/ITEM_DEL` 重建,拒绝或删除后可能仍阻塞源物品 |
| 放入金币 | `SendExchangeElkAddPacket` 只负责请求,服务器决定金额并回包 | UI 限制 1..9,999,999,native 只做 u32/in-game 检查 | PARTIAL:本地上限和已放金币判断不是 40250 客户端 authority;native 缺交易 active/拥有金币/动作门控 |
| 接受/取消/结束 | 接受和退出发包,窗口等 `GC_END` 关闭;交易结果由服务器聊天和回包提供 | 接受有本地 pending;取消和距离超限会发包后立即 `close()` | PARTIAL:取消/距离分支不等待权威 `GC_END`,真实拒绝、断线和乱序关闭未覆盖 |
| 交易安全/原子结算 | 客户端不计算交换、不改双方背包/金币;服务端负责锁定、空间、金额和最终交付 | `player_trade_exchange_system.gd` 本地计算 ready、金币、空间和原子转移 | GAP(本地 helper):这不是 40250 客户端实现,不能作为 live parity 依据 |
| Safebox 打开/关闭 | `GC_SAFEBOX_SIZE` 打开,服务端控制大小;关闭走 `/safebox_close`,窗口状态由服务端同步 | native/UI 有 open/close、页数和 10m 自动关闭;close 后本地立即隐藏 | PARTIAL:基础链存在,但本地先隐藏/请求失败回滚、乱序重开和真实服务端错误缺测试 |
| Safebox 存取/移动 | checkin/checkout/move 请求后由 `GC_SAFEBOX_SET/DEL` 更新 | 拖放使用目标槽,移动/右键取出存在;移动端 deposit 先找本地首个空位并记录 pending | PARTIAL:pending 没有与回包关联;本地 helper 直接存取/堆叠,不能证明服务器拒绝、堆叠和物品属性结果一致 |
| Safebox 密码/金币 | wrong-password 和 size/money change 由服务器回包;客户端不保存真实密码 | native 通过命令发送密码;本地 `safebox_storage_keeper_system.gd` 保存密码、冷却、锁定和金条规则 | PARTIAL/GAP:helper 的密码/冷却/金条熔铸是自定义本地 authority,不能替代 40250 server command/GC 结果 |
| Mall 打开/取出 | `GC_MALL_OPEN/SET/DEL` 驱动只读窗口,checkout 后服务器更新背包/Mall | native parser/UI/getter/checkout 存在,UI 禁止插入 | PARTIAL:主路径接近,但服务端失败、背包回包、重连清理和掉落音效没有端到端证据 |
## Implementation-equivalence matrix
| 项目 | 状态 | 证据/限制 |
|---|---|---|
| Preconditions | PARTIAL | 位置、展示槽、u32、窗口和部分 anti flag 检查存在;exchange native 缺 `__CanActMainInstance` 等阶段/动作门控,真实库存/交易 active/拥有金币由服务端决定且未在客户端等价复现 |
| Branch structure | PARTIAL | exchange/safebox/mall 的协议分支和 UI 分支基本存在;取消立即关闭、移动端首空位 deposit、local helper 结算和密码/金条旁路与参考客户端不一致 |
| Algorithms/formulas | GAP | 40250 客户端不实现交易结算、库存空间、仓库密码存储或金条熔铸公式;当前本地 helper 复制这些服务器职责,不能宣称与客户端统一 |
| State transition order | PARTIAL | native store 大体是请求→GC→dirty/signal→UI;交易 `_offered_cells`、safebox pending 和 close 先隐藏造成本地 optimistic 状态,拒绝/乱序时序尚未闭合 |
| Constants/units | PARTIAL | 12 交易槽、45 格/页、socket/attribute 槽和距离限制存在;金币 9,999,999 UI 上限、helper 45 格/单页和自定义冷却/金条常量需避免冒充 40250 client 常量 |
| Timing/event sources | PARTIAL | exchange/safebox/mall dirty signal、10m 距离关闭和密码错误 UI 存在;真实 GC_END、GC_ITEM_SET/DEL、断线、重复请求和重新打开时序缺回归 |
| Resource/data sources | PARTIAL | live item tooltip 使用协议 item 数据并保留 socket/attribute;本地交易/仓库 helper 使用脚本 Dictionary,移动端 pending/首空位选择可能在服务器结果前改变显示 |
| Protocol side effects | PARTIAL | CG exchange、safebox、mall 基础布局和 store parser 存在;exchange 的动作门控、完整错误结果、商城掉落音效和实际服务器回包副作用不完整 |
| Interruption/failure/cleanup | GAP | helper 可无服务器回包直接成功;UI 的本地关闭、optimistic 源物品/pending 集合、密码失败、背包满、断线和乱序恢复没有完整回滚/重放证明 |
## Regression evidence
- `project/test_player_trade_exchange_parity.gd`:28/28 通过;覆盖的是本地交易 helper 的距离、不可交易物品、双方锁定、金币、空间和原子交付,不是 `GC_EXCHANGE` live 回包。
- `project/test_safebox_mall_parity.gd`:37/37 通过;覆盖 fake client 的 45 格、距离、socket/attribute、checkin/checkout/move、商城禁止插入和密码命令。
- `project/test_safebox_storage_keeper_parity.gd`:37/37 通过;覆盖本地密码/冷却、库存堆叠、移动、金条熔铸/承兑和非法格位,不能证明 40250 server authority。
- `project/shop_cube_mall_test.gd`、`project/test_mall_affect_parity.gd`、`project/popup_windows_parity_test.gd`、`project/test_security_lock_parity.gd`、`build/extension/net_entity_test`、`build/extension/net_classic_session_test`:通过;主要验证 UI/helper、item-mall 辅助效果或基础 packet/store,没有完整 exchange/safebox/mall 服务器拒绝、乱序、断线和回包回滚。
- `project/complex_item_drop_test.gd`:当前 1 项失败(购买模式仍允许把物品拖到出售目标),说明统一拖放门控仍有可复现差异;该失败属于相邻商店合同,但会影响交易/仓库共用拖放边界审计。
## Deep audit round 2026-09-21
### Fresh regression results
- `godot --headless --path project --script test_player_trade_exchange_parity.gd`:退出码 0,28/28 通过;覆盖本地交易距离、anti-give、锁定、金币、空间、原子转移和取消 helper。
- `godot --headless --path project --script test_safebox_mall_parity.gd`:退出码 0,37/37 通过;覆盖 fake client 的仓库/商城窗口、45 格、距离关闭、属性显示、存取移动和密码命令。
- `godot --headless --path project --script test_safebox_storage_keeper_parity.gd`:退出码 0,37/37 通过;覆盖本地密码/冷却、堆叠、45 格和金条 helper。
- `godot --headless --path project --script shop_cube_mall_test.gd`、`test_mall_affect_parity.gd`、`popup_windows_parity_test.gd`、`test_security_lock_parity.gd`:均退出码 0;商城效果、窗口和安全锁局部测试通过,其中安全锁为 48/48。
- `build/extension/net_entity_test` 与 `build/extension/net_classic_session_test`:均退出码 0;基础 exchange/safebox/mall parser/session 通过。
- `godot --headless --path project --script complex_item_drop_test.gd`:退出码 1;购买模式出售目标门控失败,属于相邻 shop/drag contract 的持续回归。
### Static parity findings
- 40250 交易请求全部先经过 `__CanActMainInstance`,客户端只发送源位置/金币/接受/退出,`GC_EXCHANGE_*` 决定展示和最终结束;当前 native exchange 请求主要做阶段/范围校验,`player_trade_exchange_system.gd` 仍可脱离网络直接完成双方金币、空间和物品转移。
- 当前 `exchange_ui.gd` 的 `_offered_cells`、pending 和取消/距离关闭逻辑没有统一由 `GC_ITEM_ADD/DEL`、`GC_ACCEPT`、`GC_END` 重建;拒绝、乱序、断线和重新打开可能保留乐观状态。
- 40250 Safebox/Mall item 回包保留 vnum/count/flags/anti_flags/socket/attribute,并由服务端决定页数、堆叠、空间和金币;当前 live native/store 路径能承接这些字段,但 `safebox_storage_keeper_system.gd` 仍保存密码、冷却、堆叠和金条 authority。
- Safebox/Mall UI 的距离自动关闭、密码命令和商城只出路径有局部验证;没有真实 `GC_SAFEBOX_SIZE/SET/DEL/WRONG_PASSWORD`、`GC_MALL_*` 与背包回包的成功/失败/重连/断线顺序 fixture,也没有 Mall checkout 音效的端到端验证。
- `M2Client`/`ClassicSession` 的 exchange/safebox/mall 包布局基础存在,但 native action gate、交易 active/partner/金币拥有量、服务器拒绝文案、SendSequence 和关闭幂等性未形成统一合同。
### Round conclusion
本轮仍为 `PARTIAL`:交易/仓库/商城 native 骨架和局部 UI 测试通过,但本地结算/密码/库存 helper 旁路、乐观关闭/pending 状态、动作门控与真实 GC 回包回滚仍未与 40250 统一。本轮未修改实现代码。
## Active UserInterface exchange, safebox and shop review
- `PythonExchange.h`/`PythonExchangeModule.cpp` 的状态读取覆盖双方名称、金钱、接受状态、槽位 vnum/count、metin socket、attribute 和 elk mode;这些值由交易回包写入,UI getter 不承担结算。
- `PythonSafeBox.h` 区分 safebox 与 mall 的打开尺寸、slot item、删除、金钱和当前大小;`PythonShop.h` 另外维护普通商店 tab/coin/name/item,以及私有商店 stock/display position/price/build/close,不能把它们合并为一个背包字典。
- 当前 native/store 已覆盖部分字段,但交易、仓库和商城 helper 仍有本地 optimistic/persistent 状态;真实 GC 添加/删除/接受/错误、密码失败、页尺寸、私店重建、断线和重复关闭顺序没有逐包等价测试,合同保持 `PARTIAL`。
## Static review evidence
已将 `PythonExchange.h`、`PythonExchangeModule.cpp`、`PythonSafeBox.h`、`PythonShop.h` 纳入 active source 证据;本轮确认了交易/仓库/商城的独立 owner 和回包字段边界,未修改实现。
## Status
`PARTIAL`。当前端已经具备 exchange/safebox/mall 的 native 协议骨架、回包存储和主要 UI,交易物品属性与仓库商城的基本网络状态能够展示;但 native exchange 请求缺 40250 动作门控,交易/仓库 UI 存在 optimistic 状态和提前关闭,本地 helper 直接承担交易结算、密码、库存和金条 authority,且真实错误、乱序、断线和回包回滚证据不足。本轮完成审计并记录,未修改实现。
### 2026-09-22 死代码删除
以下文件只被自身测试引用、运行时从不加载,已删除;本文中以它们为实现或证据的结论作废,相关行为需按 40250 源码单元重新移植:`player_trade_exchange_system.gd`, `safebox_storage_keeper_system.gd`, `test_mall_affect_parity.gd`, `test_player_trade_exchange_parity.gd`, `test_safebox_storage_keeper_parity.gd`, `test_security_lock_parity.gd`。