Skip to content

Engineering · XODE omni

要说手机是节点,需要证明什么 ​

在智能手机上运行区块链轻节点很难,但这是个能解决的问题。真正难的在后面——一旦把它和奖励挂钩,不运行却声称运行过,就开始变得便宜得多。

“手机是节点”这句话的确切含义 ​

在移动 DePIN 应用里,“你的手机将成为节点”这样的宣传语很常见。大多数情况下,这句话的实际意思是定期向服务器发送信号。应用每 5 分钟向后端打一次卡,报一声“我还活着”,服务器数一数打卡次数,然后发放奖励。

那不是节点。那是签到表。

在 omni 中,我们在代码层面就做了这一区分。应用内部有两个状态标志位。

弱主张

直接从链上获取

没有经过后端,而是从链的 RPC 获取了最新区块。中间人是少了,但拿到的仍然是别人告诉它的数字。

强主张

手机自行验证

设备上的轻客户端亲自确认了最终性。这个数字不是问来的,而是算出来的。

在原生 smoldot 轻客户端完成 warp sync 之前,第二个标志位会一直保持为 0。没验证过,就不说验证过。这一区分,是整个应用设计的出发点。

和奖励挂钩的那一刻,问题就变了 ​

假设只要运行节点,就发放代币。从那一刻起,最理性的做法就不再是运行节点,而是找到最便宜的办法,让自己看起来像在运行节点。

开几百个模拟器。写一个只模仿心跳包的脚本。同一台设备,只把账号换来换去。既不耗电,也不费流量,只把奖励拿走。诚实运行节点的人相对吃亏,到最后,再也没有人诚实地运行了。

在 DePIN 里,难的不是让人干活,而是让人证明自己确实干了活。

心跳为什么容易撒谎 ​

最天真的心跳长这样——应用向服务器询问当前区块高度,对这个数字签名后再发回去。服务器验证签名,然后发放奖励。

那么,这个心跳实际证明了什么?只不过是“持有这把密钥的某个人,对我们刚刚告诉他的数字签了名”而已。不必是手机,不必是节点,甚至不必是应用。一行 curl 就够了。

之前

服务器
手机

证明的是 “有人对别人给的数字签了名”

之后

链P2P
手机 · smoldot验证最终性

证明的是 “这台设备亲自跟随了链”

验证在哪里进行,决定了心跳的含义

所以我们把验证挪到了手机上。轻客户端直接接入链并确认最终性之后,心跳所签名的就不再是别人告诉它的数字,而是这台设备自己算出来的结果。想要伪造,光靠模仿不行,必须真的运行一个轻客户端——而那样一来,活其实已经干了。

一层还是不够 ​

即便把验证挪到了手机上,还是会留下漏洞。一台性能强劲的设备,可以一边运行一个轻客户端,一边拿同一个结果为几百个账号分别签名。活干一次,奖励领几百次。

所以我们把防御一层层叠了起来。每一层回答一个不同的问题。

  • Layer 1 · 这个节点是否真的跟上了链

    设备端轻客户端的自主验证。光靠模仿过不了这一关。

  • Layer 2 · 这是否为真机,是否属于这个账号

    硬件密钥断言。设备需要先注册,只有经后端验证过断言的心跳才算数。模拟器大军和来回换号,都在这一层被挡住。

  • Layer 3 · 一个人最多能靠几台设备获利

    每个账号的每月上限。就算前两层都通过了,也无法无限扩张。

关键在于第三层。必须假定技术防御迟早会被攻破。为了让被攻破时的损失不至于线性增长,最后一道防线必须是经济层面的。有了上限,绕过防御所需的成本,很快就会超过所能获得的奖励。

诚实地说明失败 ​

这套结构在用户体验上有一个陷阱。奖励变成 0 的原因有好几种,但屏幕上通常只显示一种。

因触及每月上限而为 0,与因设备验证未通过而为 0,是完全不同的两种情况。前者只要等到下个月就行,后者必须现在重新认证才能解除。可如果两者都显示为“今日收益 0”,验证受阻的用户就会一直干等,白白浪费一个月。

同样的数字,原因不同,就应该显示不同的说明。因为解决办法不同。

所以后端门控返回 0 时,会连同原因码一起下发,应用再根据原因码显示不同的提示。我们把这一点作为规则写进了代码注释——设备认证问题,绝不能显示为“已达每月上限”。

电池和流量才是真正的约束 ​

如果说前面这些都是设计,那么人们会不会真的一直开着它,就完全是另一场仗了。在手机上运行的节点,必须在两项预算之内生存——电池和移动数据。哪怕只超出其中一项,用户也会卸载应用。奖励再多也没用。

早期版本每小时要用掉 90MB 以上。轻客户端搜寻对等节点的循环跑得比实际需要的更频繁,检查点也已经过时,每次要追赶的区间都很长。我们修复了搜寻循环、更新了检查点,把用量压到了每小时 50MB 左右。

这类工作成不了博客标题,但决定一个 DePIN 应用生死的,往往正是这些。没有人会因为“最终性验证”卸载应用。人们卸载,是因为流量费。

总结 ​

把手机变成节点,是三个问题叠在一起:让它跟上链的问题(轻客户端),让它证明自己跟上了链的问题(验证位置 + 硬件密钥),以及让人一直开着它的问题(电池和流量)。

三者只要漏掉一个,其余的就都失去了意义。没有验证的节点是签到表,没有证明的奖励是羊毛场,吃电的应用没人会开。

下一篇文章将讨论钱包应用如何取用这个节点验证过的结果——也就是“不向服务器询问余额的钱包”。