listtransactions 的五个 category:同一笔钱为什么会出现在五条记录里 图 1
listtransactions 的五个 category:同一笔钱为什么会出现在五条记录里 · 图 1

用钱包 RPC 对账的人几乎都遇到过同一个困惑:明明只转了一笔钱,listtransactions 却返回两条甚至三条记录;挖矿收入里冒出一个没见过类别 immature;某条记录隔了几天类别变了。这不是数据错乱,而是这条命令的记录模型与直觉不同——它列的不是”交易”,而是”交易在这台钱包视角下的每个角色”。本文按 v31.0 源码拆解每个条目的生成逻辑、五种 category 的判定条件,以及正确的对账读法。

条目从哪来:按地址归属逐个展开

源码的展开方式是这样的:对每笔钱包交易,先看它有没有属于本钱包的输出——每个接收地址生成一条 receive 条目,金额取该输出的值;再看它的输入里有没有属于本钱包的——按地址归属聚合成 send 条目。一笔”给自己内部转账”的交易可能同时产出一条负金额 send 和一条正金额 receive;一笔带找零的对外支付产出一条 send(含找零扣除后的净额逻辑)加上找零自身的记账。所以你看到”一条记录”还是”两条记录”取决于资金动了几个钱包角色,与链上交易条数不是一一对应。对账时把条目金额直接当”交易金额”加总,几乎必然和区块链浏览器的数字打架——正确做法是按交易哈希聚合后再比较。

category 的五种取值与判定

源码里能出现的取值有 send、receive、generate、immature、orphan。非 coinbase 交易的 receive 条目类别是 receive/receive;coinbase 收入(挖矿所得)走另一套判定:交易还在主链上但 coinbase 输出未达到成熟期(源码用 coinbase 成熟度判断,即默认的一百个区块成熟门槛)时标为 immature;被孤立的 coinbase(所在区块离开了主链)标为 orphan;已成熟的 coinbase 显示为 generate。钱包交易里”已深度不足一百但非 coinbase”的交易不会标 immature——immature 专指 coinbase 成熟期,这是很多矿池教程混淆的一点。类别会随链重组迁移:generate 的区块被孤立后条目转 orphan,重组回来又回到 generate 或 immature,对账程序必须允许类别是状态量而不是常量。

分页游标:skip 不是时间排序保证

命令的第二、三个参数是 count 与 skip,实现方式是把全部候选条目排好之后取子区间。列表的默认顺序按交易的区块高度与时间线索排列,但没有任何锁或快照保证两次分页调用之间列表不变——新交易到达、确认数变化都可能让 skip 窗口漂移,导致同一条目被取两次或漏取。稳健的拉取姿势是单次大 count 一次取全再本地分页,或者用 listtransactions 配合交易哈希集合做幂等合并,而不是循环 skip 直到返回空。

过滤参数对可见性的影响

第一个账号参数支持按标签过滤,数据源就是地址簿里的 label 归属;minconf 限定最少确认数,设大能过滤掉零确认条目,但也同时隐藏了未确认入账——报表口径要在文档里写明用的哪档。注意 minconf 为 0 时未确认的 send 条目同样计入,对”今日净额”类统计,一笔双花未决的 send 会暂时把数字做负。还有一个容易漏看的点:include_immature_coinbase 开关在 listreceivedbyaddress 家族里默认关闭 immature coinbase,挖矿对账如果只开 listtransactions 开、那边不开,两边数字天然差一块成熟期中的 coinbase。

对账的结论只有一句:这条命令忠实报告”钱包视角的事件流”,但它的事件粒度和类别是状态化的,任何把它当不可变账本的使用都需要先做交易级聚合和类别迁移容忍。

风险提示:本文是钱包接口机制说明,不构成投资、核算或税务处理建议,财务处理请以完整链上数据为准。