原生资产流:把调用树与净余额变动核对
一笔交易经过路由器时,同一原生资产可以移动多次。把轨迹里所有value相加,不等于用户净流出,也不等于协议新存入。应把已提交转移做成有方向的数量账,保留调用类型与回滚边界。
本题解释单笔交易造成的地址余额变化,不是解码函数名、统计内部调用行数,也不能因外层成功就说每一层经济动作都完成。
展平之前保留树
Geth内置tracer文档的callTracer部分列出嵌套frame的type、from、to、value、error和子调用,并区分DELEGATECALL代码目标与普通接收地址。不同提供方格式可能不同,应保存客户端版本和配置。
给每个frame标交易哈希和结构路径,例如root、子零、子一,导出成行后仍保留父关系。否则子调用本地成功,后来父层失败回滚整棵子树,也可能被错算为已付款。
不能只按金额或地址对去重。两次真实调用可以有相同from、to和数量;同一frame也可能同时出现在外层摘要和内部转账表。应先确定行对应哪个执行位置,再判断重复。
明确定义入账规则
只有按语义实际转移原生资产、并且效果保留到相关祖先回滚之后的frame,才形成本表的转移边。DELEGATECALL继承的上下文value,不自动是给代码地址的另一笔付款。
拟议但失败的边另列,它可解释行为和成本,却不能因为轨迹显示请求数量就变成已提交资产移动。外层成功也不免除检查内层error。
对每个地址,净贡献等于已提交流入减已提交流出。在覆盖完整的转移图里,手续费和其他余额机制另列之前,各地址贡献之和应为零。这是对账检查,不是轨迹已覆盖所有状态变化的保证。
假设主例:路由一百单位
设虚构账户A、路由R、接收者B和C,原生资产单位足够大无需处理舍入。A给R一百,R执行一次显示上下文value一百的委托代码调用,向B转七十,尝试向C转十但该调用回滚且被捕获,再退A三十。
外层交易成功。R到C的十单位不保留,委托代码地址也不因上下文显示一百而另收一百。下面均按这些明确假设分类。
| frame | 显示value | 类型判断 | 已提交数量边 |
|---|---|---|---|
| root A到R | 一百 | 成功普通价值调用 | A到R一百 |
| R上下文委托代码 | 一百 | 上下文执行 | 不由此产生付款 |
| R到B | 七十 | 成功价值调用 | R到B七十 |
| R到C | 十 | 回滚且被捕获 | 无 |
| R退A | 三十 | 成功退款 | R到A三十 |
全显示值求和为三百一十,仅已提交边的毛转移量为二百。两者都不是A净经济流出。A为三十减一百等于负七十;R为一百减七十减三十等于零;B加七十,C零。
四者合计负七十加零加七十加零,等于零。此式能暴露漏退款或重复委托边,却不能把B认作商户,也不识别这次是否出售资产;经济目的需要另一层证据。
如果同一frame在服务摘要与内部表各出现一次,应以结构身份合并,而不是把毛转移二百再翻倍。若两条相同金额有不同路径,则不能仅凭数值相同删除其中之一。重复显示和真实重复动作都可能发生。
另对余额与费用
假设A起始一百五十单位,并有独立依据确认交易费零点零二。在完整假设下,期末为一百五十减七十减零点零二,等于七十九点九八。不能把七十点零二全记成给B的付款。
B若起始十且无其他变化,期末八十;R余额不变。手续费究竟由A还是另一发送者支付必须核对,赞助等安排会改变这条对账,不能一律扣被研究者。
费用各部分也不保证全部流到同一可见地址。窄转移研究可以将已核总费用借项另列,不对无法解释残差编接收者。协议燃烧、接收者及其他分配应按相应规则单独查。
使用适合交易边界的前后状态。整块前后余额可能同时包含其他交易与系统操作,未经排除就归给本笔,会把正确的余额差解释成错误的单笔流量。
不同机制边界:子成功后父回滚
另设替代历史:R给B七十,B内部给D二十并局部成功返回;随后B回滚,R捕获失败。二十这条子调用虽然本地成功,其效果也会随父子树一起回滚。
在这条历史中,假设R最终退A全部一百。已提交边只有A到R一百、R到A一百。B与D净贡献均零,A转移净贡献也零,仍需另算交易费。
它不是原七十付款历史再加二十,而是不同执行分支。展平表若保留子success却丢父error,就会造出未进入状态的付款。因此每条候选边都要检查所有相关祖先,而不仅当前frame。
另一种情况是root整体失败。此时普通应用转移也不能因为内部曾成功就保留,费用等不同回滚边界另核。不要将“消耗了gas”和“收款已完成”混为一谈。
CALL覆盖不应普遍化
合约创建、适用规则下的自毁相关余额变化、验证者提款及其他系统机制,可能需要额外处理。只收普通CALL的数据集,不是全部原生余额变化的完整证书。
残差出现时先确定机制。Geth页面还描述状态型tracer,可协助比较变动账户,但仍需适当历史上下文。状态差证明余额变化,不自动解释经济用途,不能把一切差异都命名为隐形转账。
保存残差金额、地址和候选原因,再取缺失frame、系统记录或正确状态边界。不能静默放入“其他内部流”使总账强行平。明确未解决项比假造平衡边更有用。
区分全图与研究子集
若只报告R所属地址集合,A到R与R退A跨越边界,R到B也可能跨越;R内部另一地址之间的转移则不必形成集合净流入。先按地址对账,再按有依据的集合归并,避免把每个内部跳转都计作外部需求。
集合归属若未知,应保留地址级结果和待核边,不凭路由品牌把所有被调用地址并进一个实体。这样才能说明所谓净流入究竟净掉了哪些内部关系,而不是只有一个不可复算数字。
给实体边界一个复算例
若独立材料把R和B都归入同一实体,主例跨界流入一百,跨界退回三十,实体净流入七十;R到B七十属于该集合内部。若问题只覆盖R,流入一百被流出七十加三十抵消,净额零。相同执行图可以有两个正确结果,差异来自明确边界,而非哪套工具更准确。
先保存地址级净额,再按有依据成员归并。成员未知就保留候选集合与金额影响,不能仅凭路由品牌把被调用地址全部纳入。发布时说清哪些边被内部抵消,别人才能复算所称协议净流入。
替代全额退款历史中,若A仍付假设零点零二费,起始一百五十会变一百四十九点九八。转移贡献为零而费用仍扣,这两个事实不能压成给B付款。root失败等其他情况也应按真实回滚边界另外核,不能从一个成功退款推整笔免费。
金额解析还要保留原始量纲。比如独立测试中的十六进制0x64是十进制一百,若tracer单位为wei,就是一百wei,不是一百ETH。先解码原始整数、再换研究单位,能防止数值守恒看似成立却整体差十八位尺度。主例的一百是明确假设单位,没有冒充真实原始响应。
净额总和零也不能自证完整。若把R到B七十整条边漏掉,A仍负七十、R变加七十、B零,合计仍零。实际R余额未变、B增加七十,才能暴露漏边。守恒和实际地址状态必须一起核,不能用一个恒等式认证全部来源覆盖。
可执行对账表
| 操作 | 记录 | 能发现的错误 |
|---|---|---|
| 固定执行身份 | 网络、交易、区块、客户端 | 混链、混分支、混格式 |
| 保留层级 | 路径与父frame | 回滚后代被入账 |
| 分类语义 | 调用类型与价值效果 | 委托上下文重复扣款 |
| 记录保留结果 | 当前error与祖先结局 | 捕获失败被当完成 |
| 计算地址净额 | 已提交边的流入流出 | 毛跳数混用户流出 |
| 对余额 | 状态边界、费与其他机制 | 所有差额归单笔付款 |
tracer更正、父错误补回、链状态改变或系统操作识别后,应重核。分别发布毛已提交跳转量、地址净贡献和手续费,解释真实执行路径,而不把调用树长度夸大为存入或用户需求。
常见问题
所有内部value相加能测原生流入吗?
不能。它们可能是连续跳转、失败尝试或上下文,需要先分类再算目标净流。
子调用成功是否在父回滚后保留?
相关父层回滚会移除其效果,需要保留祖先关系判断。