让笔记本也有一个可拨通的地址

p4 通过拨号找到节点派发工作。NAT 之后的机器没有可拨的地址,而大多数机器都在 NAT 之后。让这样一台机器仍能加入,需要什么,以及它加入那天的实测数据。

挡路的不是意愿,而是方向。 p4 通过*向*节点打开 TCP 连接来投递工作。节点自己发起的连接只用于回执 —— 落在其上的 Data 帧会被 peer_closed("unexpected frame on outbound hop") 断开。笔记本向外打开隧道并从中接收工作,这件事不会发生。靠意愿不行,靠重试不行,靠更好的客户端也不行。

约束

贡献者的机器默认不可达

家用机器坐在网络地址转换之后。它能出到互联网,互联网进不来。我们自己的机群同时呈现了两面:办公室的 GB10 直接挂着公网地址,两台 MI250 则在同一个共享出口之后的 192.168.20.x。MI250 并不特殊,贡献者的机器正是这个样子。

p4 里没有路由表,没有会合服务,没有打洞。deliver_outbound 从事件信封里取出地址并调用 connect。就这么多,在机柜里这是合理的设计,只是笔记本无法满足的设计。

答案

一条向外的连接,一直开着

中继代节点持有一个公网地址。节点只维持一条通往它的向外连接,自己什么都不监听。当有人拨通那个公网地址,字节就沿着节点早已持有的连接流下来。p4 的两端只看到一个普通地址上的普通套接字,谁都不知道中继的存在。

拨号方看到的,和节点实际运行的

caller  ->  tcp://relay:43100        # 一个普通的 p4 地址
            |
            +-- relay      公开。只转发字节,不解析
                  |
                  +-- tunnel   节点自己打开的一条向外连接
                        |
                        +-- p4-agent  127.0.0.1:42031,只监听回环

中继不读 p4。载荷按字节原样转发、从不解析,所以它分不清 LOAD 和 INSPECT —— 而且它不该分得清。一旦它理解了流量,它就成了能够改动流量的东西。

为什么它同时是安全边界

端口转发从来就不是选项

p4 没有任何认证。没有 TLS,没有令牌,没有白名单 —— 任何能触及代理端口的主机都可以发送 NODE_LOAD、NODE_UNLOAD 和 INSPECT。要求贡献者把家用路由器的端口转发进去是无法辩护的,这正是最显然的解法恰恰是错解的原因。

经由中继,节点什么都不监听。它保持一条向外的连接,并在这条连接承载任何字节之前先证明运营者钱包 —— 用的是结算网关早已使用的同一套 ed25519/base58 签名,于是节点注册的身份就是奖励账本据以记账的身份。可达性与认证,原来是同一个答案。

生效的那一天

测量,而非宣称

6 ms
机群主机到 NAT 后的 Mac
28 ms
代理应用自身规则所用时间
512 KiB
逐字节一致地往返
0
笔记本上开放的端口数

数据中心里的一台 MI250 主机拨通 tcp://34.50.62.159:43100,在 6 ms 内触达了运行在 NAT 之后一台 MacBook 上的 p4 代理。28 ms 后代理断开了连接,因为第一帧不是 Hello —— 这是它自己的协议规则,由它自己执行,而这台机器片刻之前还根本联系不上。那次拒绝就是证据:中继不解析载荷,因此不可能是它做出的。

检查项结果
512 KiB 往返逐字节一致,177 ms
256 KiB 往返逐字节一致,108 ms
三路并发拨号流之间没有串扰
未签名的注册拒绝
重放的挑战值拒绝
另一半

节点得有东西可跑

背后没有引擎,可达性就毫无价值,而桌面应用从未随包携带引擎:它在三个构建目录里找 p4-agent,只有在已经手动编译过 p4 的机器上才找得到。现在应用为它所发布的每个平台携带自己的二进制。

  • macOS:合并两种架构的 universal 二进制。Mac 应用以 universal 打包,资源被原样复制进两个切片,因此只放 arm64 会让 Intel Mac 用户拿到一个看似完整却启动不了节点的应用 —— 一种只在开发者没有的硬件上才显现的故障。
  • Windows:交叉编译,并让查找去要 p4-agent.exe。忘记扩展名,正是 Windows 构建找不到自己代理的那条路。
  • 每次打包都先构建代理,因此不可能产出没有代理的发行版。

还做不到的事。 节点现在能跑,也能被拨通,但推理仍然不给它带来收益。引擎会发出逐阶段的工作记录,却没有东西去收集;结算网关的贡献度端点 —— 被正确地拦住,使任何节点都无法给自己记账 —— 从未被调用过。运行和获得报酬是两个问题,眼下解开的只是前一个。