notfound 消息:比特币节点要货被拒时那句明确的"没有" 图 1
notfound 消息:比特币节点要货被拒时那句明确的"没有" · 图 1

比特币节点之间的大部分对话是沉默的:发了请求没回音,多半只是对端还没准备好。但有一类回复专门打破沉默——notfound,字面意思”没找到”。它是 getdata 点货单的对账单:你要的东西我手里没有,明确告知,不用干等。核心源码 protocol.h 里定义着这条消息的类型常量,net_processing.cpp 则在处理每条 getdata 时决定哪些条目要写进拒绝清单。本文拆它出现的三个位置、它没承担的职责,以及把它误读成故障的常见弯路。

消息在哪出生

对端发来一条 getdata(可能点交易、点区块、点见证版区块),节点逐条查手里的库存。源码路径很直白:处理函数把请求条目逐个尝试取出,取不到的推进一个 vNotFound 向量,全部处理完若清单非空,就打包成一条 notfound 发回,注释里还专门说明正常运维中常为请求数据的父级交易回 notfound。换句话说,它不是错误警报,而是库存盘点结果:一次性把”这单里哪些缺货”整体回执,让请求方立刻知道哪些条目需要转向别的对端。

notfound 消息:比特币节点要货被拒时那句明确的"没有" 图 2
notfound 消息:比特币节点要货被拒时那句明确的”没有” · 图 2

它配合的两类等待

第一类是孤儿交易与 SPV 钱包的依赖遍历。交易引用一个本地还没有的前序输出时,节点发 getdata 点前序;对端若从未见过这笔前序,就回 notfound,请求方由此判断这条依赖链在当前对端补不齐,转而去别的连接重试或降级处理,而不是让交易在”疑似即将到达”的状态里挂死。源码注释点名了这类受众:SPV 客户端靠这条消息在递归梳理未确认交易依赖时不再空等,其他节点也据此转向别家重取。第二类是区块侧的沉默变体:对区块的 getdata,若请求的区块根本查不到、或不在本节点允许提供的链上,实现选择不回 notfound 而是直接沉默——请求方靠对端超时与切换候选源来补洞,不靠这条消息。两类场景的合起来定义了排障地图:交易缺货有回执,区块缺货只换源。

它没承担的职责

notfound 只回答”我这个连接此刻没有”,不回答”全网没有”,更不代表请求非法。同一份数据从别的对端很可能正常送达;对刚广播几秒的交易,收到 notfound 再收到数据是正常时序。它也不携带任何拒绝原因码,消息载荷就是一串与 getdata 同格式的物品清单,没有理由字段。把 notfound 等同于”这笔交易被网络拒绝”是最常见的误读——被 consensus 规则拒收的交易连 inv 公告都见不到,轮不到点货环节。

排障时的正确姿势

日志里 notfound 偶发出现无需处理,它是协议正常运转的心跳。需要警惕的形态是密度异常:如果节点对某类物品持续大量收到 notfound——比如所有对端对同一批区块一律缺货——更可能是自己停在落后分叉上,请求的区块在健康链上根本不存在;此时核对链尖高度与状态,比逐条追查消息更有用。反过来,如果自己的节点对上游请求方大量回发 notfound,常见原因是刚重启、内存池按配置未恢复或区块存储滞后,属于启动期正常再同步。诊断口径一句话:偶发缺货单是常态,方向一致的批量缺货才是链位或配置问题。

快速问答

问:notfound 是比特币改进提案编号吗? 答:消息本身是核心实现与参考协议文档中定义的通用消息类型,按源码与协议说明为准;网络上流传的编号化写法不必当真。

问:删除数据目录重同步期间收到大量 notfound 正常吗? 答:正常,节点库存不全时对外请求常缺货,回 notfound 属诚实应答。

问:钱包能收到这条消息吗? 答:轻客户端不一定直接收发它;多数情况下由其后端全节点消化后表现为换源重取。

风险提示:本文解释节点协议消息行为,不构成投资建议。生产节点排障请以所用版本文档与源码为准。