最初的message设计
这篇文章讲一讲在区块链基础设施设计中,我认为存在的一些基本错误。简单的说,我们错误的把设计重心放到了状态修改的原子性上,而这里更好的替代方案是使用消息(而非状态)。错误的设计影响深远,并且对于非计算机行业的人群更加难以理解。
下面我们用一种通俗易懂的方式,针对非技术领域的朋友,解释消息和状态方案之间的区别。我会尽力简单地描述什么是消息, 在深入之前,你可以先把消息理解为我们发布出去的一篇博客文章,虽然这个比喻比较简陋。
什么是状态机?
状态机是一个软件装置,通过它,我们能在某一时刻得到一个确定的状态。换句话说,如果状态机初始状态一致,当我们输入同样的数据,那么状态机的输出结果肯定也是一样的。
我们可以把状态机比做一个自动贩卖机的系统软件,贩卖机的软件部分决定了硬件可以执行的行为。如下图,当贩卖机处在状态1下,它在等待用户投币。如果被投币,贩卖机进入状态2。在状态2下,贩卖机会等待用户点单。如果点单按钮被触发,则投递对应的饮料,最后回到初始状态1。本质上所有贩卖机都运行着同一套代码和内存数据, 这保证了我们可以通过算法准确获取到某一刻机器的实时状态。同时,它在感知到外部传入的消息后(如投币,按点单按钮),能够根据算法和当前状态产生确定的行为(如投递饮料)。
我们可以通过组合小的状态机,形成一个大的——数据库本质上就是一个大型状态机,数据库的表、表内的每一行、每行的每个属性,都可以看做为状态机。协议则是一对状态机,两头一边一个。区块链,则是另一个庞大的状态机,它由成千上万个“全节点”状态机组成,每个全节点还挂靠着成大量轻量客户端(SPV client)。本质上,状态机的设计很简单,但是如何将小的状态机组成一个大的,这是一门科学艺术(没有固定方法)。对这里的复杂性,我们不多作展开。
选型
创建一个状态机,一般有两种方式。
请注意,后面我以一种要求不太严格的、比较个人化的方法来描述。首先为了简洁,我们刻意省略了代码部分(IF语句)和输出部分(DRINK)。毕竟这篇文章的主要目的,是帮助并非geek的你,理解消息是什么。
我们设计的状态机通常如上图所示——从状态1开始,然后接收到消息1。对消息1的处理,使状态从1变成2。当它的状态由2变成初始状态1的过程中,会视情况发送消息出去。
实现一个状态机,主要是通过代码去控制保存交易状态,有消息过来,代码能控制已知状态间的转换。 实现它的过程中,最终我们发现本质上有两种构建方法。这两种选型的碰撞,极大丰富了我们的思考和设计,最终增强我们的技术能力。
方法一:创建一个状态机,先保存初始状态1。当消息到来,状态由变成2,然后保存状态2。一直不断重复!我们所有要考虑的,就是保存好上图两个蓝色圆圈所表示的状态,其他的任何信息都可以被忽略掉。
方法二:创建一个消息机,消息机启动时都在状态1。当输入数据(如上图的红色药丸,即消息)被灌入机器,我们将消息记录下来,然后输出新消息。这种场景下,我们保存了消息丢弃了状态,因为状态可以随时被计算出来。
实际上,因为机器的运行结果是可预测的,所以两种选型理论上很接近。这样的消息机一旦被创建,那么它的执行结果都可以可预期的,例如上图我们输入M1,一定会使消息机状态从State1转换为State2,最终输出M2。
接着,如果我们再拿一个一样的状态机,输入与刚才一样的消息,就可以得到一样的最终状态。再或者,如果记录所有状态,我们也能根据状态的前后变化,反推出它导致的执行动作,而导致状态变化的消息也不再重要。 大家都学习过几何,这其实就是一个“存储点”还是“存储边”的问题。
上面提供了区块链基础设计的两种思路。通过对我们的需求进行分析,可以决定选择其中的一个:
数据库是一个状态机,它就像一个开关——知道自己是开着还是关,但并不知道在这之前变化的过程。在与数据库通信时,协议就充当了消息机的角色。例如我们用email通信,拿到最后一个邮件并不能得知整个对话的经过,但是通过扫描两个人的来往消息,就能得知具体过程。
区块链的底层模型究竟选哪一种?
我们要说的是理论上的,而实际很可能不一样。比如,你的银行账号看起来就像一个状态机,里面存储着可以随时查看的余额。但是在银行内部,因为他们统计时采用复试记账,这看起来更像一个消息机。
区块链现在应该怎么做?
有可能是历史的原因,或者是因为惯性思维,区块链更多的被当做是一个状态机,而非一个消息机:
......区块链的主要功能,是维护一个可以被并行修改的状态。为了解决并发读写冲突,区块链把状态记做账本(LEDGER),账本中包含了一系列对初始状态的更改。区块链把这些更改记录到一个区块(BLOCK)当中。在比特币的例子中,区块链保存的状态通常指未被花费的余额(UTXO)。
——LM Goodman, "Tezos: 一个自动修复的加密账本立场文件",2013
再或者,我们来看最近的一个以太坊替代项目:
如何让交易语义和合约更好的融合在一起?
在消息处理的角度上看,交易是指在某条链上,它已经被见证过并且通告(给所有节点)。
消息只是一个被传输的虚拟的数据结构,但当它被发起方传递给合约后,将导致合约前后状态发生实际变化。而这个过程被另外的节点见证,最终将被打上时间戳,记录到存储器(区块链)中。
消息的传递是一个原子操作。不管一个消息是否被见证,只有被成功见证过的消息才会被记录到一个区块当中。
——anon?,“RChain架构-合约的设计”,2017 RChain Cooperative
我们留意到,上述作者创造了一个完整的、以存储交易消息为基础的区块链,却最终落地变回了典型的状态型区块链。
另外一个例子,如下图,我们看一看比特币的UTXO状态机模型(UTXO是用户未花费余额的状态集合)。在UTXO模型下,交易是一条对状态的记录,里面包含了一组输入和一组输出。对比上面的图3,我们只关注两个蓝色圈中表示的状态(忽略红色的消息)。通常每一笔交易就像下面的蓝色方框,从左边有一组状态输入,右边有一组新状态输出。
方框左侧输入的,是其他交易的输出。输入后,就代表他们被花掉了,并且从右侧输出一组相应的可用余额,输出的余额可以在未来被交易。上图中,TRANSACTION 1输出了0.5个比特币,TRANSACTION 2消费了刚才输出的0.5个比特币。
比特币的一个交易记录,记录了若干输入和输出,就好比一个小型的资产平衡表,输入的余额必须等于输出的余额。 交易记录就好比乐高积木,新的交易记录必须对接到旧的交易记录中,并且开放给更新的交易记录对接。
UTXO的脆弱性
我们很早就知道,但是这里我还是要重提一下:比特币的设计精妙非凡,但是从另一面看,它的很多组件相互的关联性太强了。如下所说:
“一个纯粹的P2P版本的电子现金,能够允许点到点的在线支付,这个过程绕过了传统的金融机构。”
中本聪,“比特币白皮书:一个点对点的电子现金系统”,2008
(变成流通)货币是比特币的目标,同时,货币也是维护它安全性的驱动力(通过奖励竞争挖矿的获胜者)。货币和安全性之间的强依赖,导致了比特币在架构上存在脆弱性。这里并不是说比特币会因此而崩溃,我们想说明的是,如果我们更改比特币系统中的某个设计,这可能导致(比特币) 的架构不再像之前那样经得起推敲。
同样的问题存在于UTXO。我们刚提到,比特币的目的是想成为流通货币。对于全节点来讲,它需要记录下当前时刻的每一笔可用余额,这样才能保证它独立验证所有交易,并且打包出块以获得奖励。相反的,对于SPV轻量节点或者其他远程轻量客户端,则只需要提供一个简单的方法证明有他能花这笔钱,完全无需将整个链(中交易输出的UTXO状态集合)记录到本地。
上述两个需求通常是矛盾的(在一个节点上,同时满足货币的流通性和安全性)。对于比特币这样拥有海量交易记录的链来说,UTXO的设计非常精巧,在一定取舍下满足了两个需求,同时保证了性能。当客户需要证明自己有一笔未花费余额(UTXO)的时候,它的优点就体现出来了。
An Order Book
如果上述两种需求发生变化,UTXO还好用么?例如我们想做一个交易所。综合很多因素后,我们发现最好的实现方式是把所有信息聚集起来,将买卖双方的报价组成一张清单,然后通过一个竞拍程序, 根据大家的报价找到一个最优方案进行撮单。当然也有其他方案可选,不过这已经是被众多交易所实践过,并且经受了时间考验的最优方法。
试想当在UTXO状态机下,有一批未知数量的买家和卖家。这里有两个需求因素制约着UTXO,让其难以发挥优势:1.不同交易员需要根据买入量、卖出量、价格随时动态撮合多方需求,达成多方满意的订单结果;2.撮合交易对消息非常敏感,当交易员得知新底价后,如果有办法他一定会撤销和撕毁之前的报价。这两个需求基本上是矛盾的。
基于消息流的设计可以轻易解决这个难题。如果链上的撮合者(在POW场景中指矿工,DPOS场景中指出块者)有稳定的买入和卖出订单消息流,它只需要简单地将接收到的订单全部放入一个“撮合合约”中,然后撮合价格匹配的订单达成交易,然后以消息的形式输出合约的执行结果。
这个过程中,消息都被日志记录下来,但是状态(例如UTXO)则是隐式的,这意味着状态只存在于机器内部的,无需(永久)保存下来。只要链上确定了消息的约束集(如消息如何输入、如何排序执行),那么同样的合约,部署在任意节点上,当我们输入同样的消息,合约执行结果一定也是一样的。
(消息机)还有两个优势:第一,当前块未确认的交易会自动延迟到下一个区块中进行确认。因为每一个传入的交易消息都是与时间无关的独立的一笔交易。而对于UTXO,每一次交易的输入和输出都会影响UTXO集合的状态,所以每笔交易都强依赖于当前时间点的UTXO状态集(每次均需重新判断)。
第二个优势,这种设计能反馈更多的(撮合交易订单的)问题。例如,当你和我想达成一笔交易,我们只需要把买入和卖出消息发送到交易合约中。这里,最复杂的一部分(撮合交易)被合约解决了,而合约的作者在设计的时候已经解决了这个问题。相反的,我们来看UTXO合约,如果你和我想按照figure5中蓝色方框内的内容达成交易,UTXO将最复杂的一部分(撮合交易)交给了交易的用户,而最简单的一部分(把记录消息到日志里)则放到了区块链上去解决。
更多的,你还可以对比在两种设计下,交易费处理上的不同。
(消息机模型的)一些疑虑
并不是说消息机就很完美,状态机的优势是跟踪定位bug更快。状态机下每一笔交易都必须对当前状态达成共识,而消息机只确认消息达成共识。对于撮合交易来讲,能够快速捕获到(状态不一致的)问题是一个很大的优势。这对于致力于削减成本和运营风险的金融部门来说,非常重要。
但是即使有这个优点,当区块链发生bug时,往往还是会产生硬分叉。
节点间的不一致,整条链都会陷入瘫痪。
当消息机模型的区块链发生bug时,由于bug都是隐式的,最多导致不同的节点对于相同消息的产生不一样的处理。受到bug影响的人可以把这条消息隔离开,然后将消息放到线下进行跟踪和重放,进一步观察bug是如何发生的。
结论
要组建一个广为人用、且可靠的区块链,消息机模型在很多方面都优异于状态机模型。但并不绝对,因为状态机在跟踪异常上更快。
虽然我们可以写一篇更全面的分析贴,列举两种模型间更多的优缺点,但本文只重点讲比较重要的几条。基于消息链模型除了有以上灵活的特性外,还能获得更高的运行效率。例如@danthemann设计的bitshare和steemit都建立在消息机模型上,两个系统均能达到1000以上的TPS。我自己研发的Ricardo系统也类似,即使它不是区块链项目,但却解释了为何我如此偏爱消息机。
理论上,消息机模型确实能提供一个更高性能的解决方案。同时,可能大家已经隐约知道,EOS项目就是基于消息模型构建!实际上,正是因为对(区块链链上数据处理)速度的需求,驱动着设计师@danthemann和我发现并且认识到(鉴谅我引用Marshall McLuhan的至理名言):
the message is the medium.
hello,请问你微信多少?加我们eos 开发者群不?
可以啊,不过这里不太方便公布微信号,要不先加个电报可以吗