×
加载中...
深度剖析aelf技术核心
2018-09-27 16:51

市场的波动左右着大众心理上的起伏,6月至今,底层公链间经历了各方媒体口中的剑拔弩张再到百花齐放。二级市场的回落也给所有的项目敲响警钟,毕竟价格只是一层糖衣,真正的价值体现注定会回归在技术本身。到今天,机遇或是挑战已然不是大家所争论的焦点,弯道超车的快捷方式也全然不适应这个行业,解决也渐渐的大过于创新。无数的思考、推翻、重构、与颠覆认知才会让行业的细流不断。


公链技术的痛点究竟在哪?如何解决?怎样破局?带着这些问题,我们听到了aelf技术副总裁戎朋在UDC2018区块链开发者大会中提出的一些解决方案。

 

谈及区块链技术,我们都认为它具有重塑及颠覆各行业的能力,然而,至今的区块链技术仿佛少了些“灵魂”,那这一个丢失的“灵魂”究竟是何物?结合二三十年来互联网行业的发展轨迹来看,我们发现,“网络效应”正是我们所寻找的“灵魂”,一个大的网络效应需要支撑起成百上千个Dapp,而一个Dapp上也可能存在着百万级别的用户,要支持如此大规模的网络基础设施,我们必须利用IT行业几十年来发展出来的技术与工程经验,这是所有做公链的项目方都应该意识到的问题,也是aelf设计中所秉承的原则。

 

为了推进区块链价值的实现,aelf识别出了区块链目前亟待解决的三个主要问题:

 

资源隔离问题

 

虽然因为网络效应,众多dapp运行在同一个区块链生态里面更有价值,但是实际上不同的dapp对于存储、计算以及网络带宽等资源的需求是各不相同的。所以我们必须实现资源隔离,使得这些dapp可以相安无事的运行在同一平台,而无需为了共享的资源而竞争。如果因为某款爆红的游戏使得你的保险交易无法被处理是有问题的。

 

性能问题

一个区块链或dapp的使用量是很难预见的,随着dapp的使用量增加,系统的处理能力也必须要能与实际的需求匹配才行。所以我们需要一个可伸缩的处理能力,从而满足逐渐增长的需求。

 

治理问题


区块链是一个开放的去中心化平台。而且一旦启动,它就获得了自己的生命。一个区块链平台要想能够长期健康的存在下去。它必须具备自我演化的能力。这个问题的关键是要有一种机制使得利益相关方能够以一种健康协作的方式进行决策,从而使得整个生态系统可以朝着有益的方向演进。而且系统最好也要能够自动的执行已被批准的更新。

 

基于以上三个行业痛点,aelf也提出了对应的解决方案:

 

DPoS共识协议


通过DPoS共识协议,所有利益相关方都可以投票选择委托节点,委托节点会负责执行交易、打包区块以及共识产生合法的区块链等事项。所有利益相关方也会通过投票的方式来决定是否更新系统合约。整个链的运行规则是被定义在系统合约里的,通过投票的方式,规则可以通过预先定义好的方式进行更改,而不会发生区块链的硬分叉。DPoS主要是解决治理的问题,一定程度上DPoS也可以提供更好的处理性能,因为通过DPoS公式,委托节点的出块时间是预先确定好的,而且间隔非常小。

 

主链+多侧链,一链一场景


这种主链加多侧链的结构是aelf独有的设计。这主要解决资源隔离的问题。设计上,一条侧链支持一个场景,并且每条侧链有独享的一套资源。这也一定程度上增强了整个平台的处理能力,因为当一条侧链出现拥堵的情况,不会影响到其他侧链。主链的用途是索引侧链产生的区块,促成跨链通信。

 

并行执行


除此之外,aelf还致力于实现智能合约的并行执行。一些特别受欢迎的合约可能在同一个区块内有很多个交易。并行执行使得这些交易可以被同时执行,而不受制于单台计算机的处理能力。

 

模块化


在aelf的设计里,每个构件都是模块化的并且是松耦合的。这种设计使得当某个构件成为瓶颈的时候,我们可以独立的扩展此模块的能力。

 

基于集群


aelf的构件运行与集群内,结合模块化的设计,aelf内部各模块的处理能力都可以伸缩与优化。

 

针对这些解决方案,戎朋在会议上向嘉宾介绍了一些具体的实现细节(灰色部分):

 

【DPOS机制】

 

戎朋:共识机制号称区块链灵魂,共识机制决定了记账权,影响了交易速度,所以首先介绍一下aelf的共识机制。

 

aelf使用的DPOS共识机制来保证高频稳定的生成区块。DPOS共识机制主要分为两部分,首先我们必须通过所有持币者投票选出生产节点,其次系统需要设计一个机制安排获选节点的顺序。今天我们会着重讲一下系统对于获选节点的排序机制。顺序计算的主要目的是为了使得各个节点的挖矿顺序是随机的,从而降低恶意节点对网络的影响。aelf网络一开始会有17个生产节点,之后每年会增加2个节点。每一轮中所有获选的节点都会被分配一个时间槽,获选节点需要在分配的时间内生产出区块。每一轮完成之后,会有一个额外区块,用来计算下一轮的顺序。在每一轮中,所有的生产节点都会产生一个秘密的随机值并公布这个随机值的哈希,在当前轮结束时,所有的秘密值也会被公布,这些秘密值被汇总在一起,用来产生下一轮的顺序。

 

【并行执行】

 

戎朋:刚才提到AElf要解决性能问题,我们将并行执行的思想引入AElf。在区块链中通常情况下,为了一致性,交易是被严格顺序执行的,那AElf中的并行是怎样实现的呢?

为了更好的理解aelf架构的意义,让我们来看看一个以太坊节点跟一个aelf节点的比较。

 

在一个以太坊节点里面,主要构件包括:

 

a. 包括一个p2p网络模块用来处理网络信息


b.一个miner模块,用来安排区块生产


c.一个worker模块,用来完成实际的交易执行


d.一个db模块负责状态与其他数据的存储


所有这些构件都存在于一个进程里面。与此对照,一个aelf的节点,虽然还是被称为一个节点,实际上它是一个集群。

 

其组成部分,除了一个负责处理网络信息以及安排区块生产的主程序之外。其他两个部分,db和worker都各自是一个集群。这种架构的好处有以下几点:


a. 首先因为worker并不是一个,所以这些worker能够同时处理多个交易


b. 其次因为db和worker都是集群,它们的处理能力都可以很方便的扩展


c.因为这样模块化的架构与部署,我们可以很方便的单独监控并优化各个模块的性能

 

接下来,让我们看看aelf是怎么实现并行执行的。我们可以想象在aelf的设计里有两层并行执行。

 

a.首先是链这一层的。我们可以把这一层看作是设计时的并行。开发者在设计与编写智能合约的时候会考虑到哪些合约是属于同一个应用场景的。他们会把有联系的、属于同一个场景的合约部署到同一条链,而没什么关系的合约会被部署到不同的链上面。

 

b.另外一层就是链内的并行。虽然发往同一条链的交易有可能是有联系的,但是我们认为这种情况发生的概率是很小的。大部分时候,很多交易都跟别的交易是不相关的,没必要顺序执行的。比如,我们考虑运行在一条侧链上的一个电商dapp。我们没必要非等到Alice购买面膜的交易被处理后才去处理Bob购买一本书的交易。所以我们认为,区块链上面的高流量更可能是来源于大量的用户发送的少量交易,而不太可能是少量用户发送的大量交易。所以,交易这一层的并行是有意义的也是值得去做的。为了实现这一层的并行,我们必须要分析所有的交易,然后决定它们使用的数据是否有冲突。如果它们使用的数据没有冲突的话,它们被执行的顺序并不会影响它们各自的执行结果,所以它们是可以并行被执行的。

 

那么,这就引出了一个问题。我们如何确定交易之间是否有数据使用的冲突呢。


我们可以把区块链想象成一个通过合法交易促发的状态转移机。那么,这里面有三个角色。一个用来储存状态的数据库,一个定义状态转移逻辑的智能合约,以及作为输入与发起方的交易。因为区块链的状态数据是在数据库里面以键值对的形式储存的,而合约里面使用的数据形式是合约类的字段。所以我们需要一种机制来将合约的字段翻译为数据库数据的key。


我们定义了一个被称为data provider的类来负责这个翻译工作。在合约的字段里,有可能有两种类型的字段。一种是普通字段,这种字段只是简单的拥有一个值,而这个值可以通过字段名来定位。另外一种字段是Map类型的字段,之中字段本身也是一个键值对的集合。每一个合约实例我们会分配一个root data provider,这种root data provider会负责整个合约所有字段的翻译。对于普通字段,我们可以直接得到一个最终唯一的Key,但是对于map类型的字段我们办不到。


所以,对于map字段的翻译我们得到的不是最终的key,而是一个sub data provider,这个sub data provider会负责在运行时将实际使用的这个map里的data key翻译为数据库的key。被翻译出的结果,我们称为path,它指向的数据我们成为资源,这样方便我们在讨论并行执行时没有歧义。对于一个普通字段的path,一个标准形式包含chain id,合约地址,data provider名,以及字段名。而sub data provider,我们可以认为翻译后的sub data provider名是root data provider名加上map字段名。


完整的path被用来唯一确定交易所使用的资源,普通字段的path可以通过静态分析合约代码得到。而map对于map字段,因为实际的data key是未知的,所以我们只能静态的得到一个partial path,在运行时我们可以通过从交易提供的数据动态的提出使用资源的path。所有使用资源的full path我们称为resource set,当我们得到所有合约的resource set之后,我们就可以分析交易之间是否有共用的资源。如果两个交易有共用的资源,那么他们就会被分到同一组。有共用资源的同组交易只能被顺序执行,而不同组之间可以被并行执行。

 

这里有一个特殊情况需要考虑,就是当合约方法有调用别的合约时。这种情况下,那么被调用的(callee)函数的resource set会被加到caller函数的resource set里,这样来构成交易最终的resource set。

 

1.总结来说,并行执行的整个流程涉及以下几个概念:

 

a. Metadata:我们需要从合约里提取出resource set以及合约之间的调用关系

 

b.Call graph:一条链的所有合约调用关系会被保存在内存里的一个全局的call graph里。当有新的合约部署或旧的合约代码更新时,这个call graph会被更新

 

c.资源提取:我们通过metadata与call graph通过静态与动态的方式提取出每个交易使用的资源。对于partial path来说,合约里所有涉及的地址都会被分别append到partial path上,从而确保不漏掉一个可能的resource。

 

d.分组执行:交易通过resource set来分组,不同的组被并行的执行

 

让我们来看看实际的例子。这是一个最简单的token合约。它有几个普通字段,用来记录token的一些基本信息。一个map字段用来记录用户的余额。这个合约有两个方法,一个Initialize方法用来初始化合约信息。一个Transfer方法用来做用户账户之间的转账。

 

我们可以看到Initialize方法会使用所有的普通字段。这里列出来了这些普通字段的Path。我们可以看到,组成Path的各部分:chain id (1234),合约地址(abcd),data provider名,字段名。

 

Transfer方法使用到的字段值有余额字段Balances,我们可以看到这个余额字段产生的path的形式,这是一个partial path。它的最后一部分user account address由交易动态的提供。

 

我们可以从下面的示例交易中看出最终产生的path。我们看tx 1,从aaaa用户转账给bbbb用户的交易。这里涉及到的是sender和receiver的地址,所以最终的path会由把这两个地址append到partial path产生。

 

2. 这是另外一个例子,这个合约是一个众筹token的合约,它会调用native token合约,用户调用这个合约的Contribute方法时,会同时向这个合约转入native token,并且该用户在此合约上的余额会按比例增加。

 

所以跟Contribute方法相关的metadata除了resource set之外,还包括对于native token的Transfer的调用关系。

 

而对于调用Contribute的方法的交易,产生的resource set不仅包括Contribute直接产生的resource set,也包括被调用的native token上的Transfer方法产生的resource set。

 

所以Contribute的完整resource set,是user account address分别在两个合约上的余额。

 

 

3.这一页展示了我们是怎么做分组。我们有4个交易,让我们来看看当我们一个一个增加交易时,分组是怎么产生的。

 

首先,我们有一个交易,从aaaa用户到bbbb用户的转账。这里涉及到的资源有两个,分别是aaaa和bbbb在native token合约a上的余额,tx 1在这两个资源之间建立了一个link。如果有别的交易使用这两个资源的话,它就会跟tx 1分在同一组。

 

tx 2的交易跟tx 1类似,但是发生在另外两个用户之间。因为它们没有资源冲突,所以这时tx 1与tx 2会分别属于不同的组。

 

接下来我们加入一种不同类型的交易。比如,aaaa用户在同一个区块里参与到众筹token中。他在发送tx 1的时候同时发了一个tx 3到众筹合约c的Contribute方法,这个交易会涉及到用户aaaa在两个合约上的余额,所以tx 3在aaaa账户在c合约和a合约上的余额之间建立了一个连接。所以此时tx 3会和tx 1分到同一组。而tx 2还是属于另一个组。

 

我们再继续加入别的交易,假设我们此时发现了一个交易tx 4,此交易是要在cccc和aaaa之间转账,这个交易会在这两个用户在a合约上的余额之间建立一个连接。这种情况下,两个组就被合并为一个组了。所有这4个交易都不能被并行的执行。如果我们没有交易tx 4的话,那么tx1、tx 3的组可以与tx 2的组并行执行。

 

4.分组的概念可能比较简单。但是,在实际的实现中,有更多的东西需要考虑。

 

这一页里,我们列出了几点考虑与进行中的改进:

 

a.首先为了奠定分组的基础,我们需要从合约代码里提取metadata,而这要求我们不能给与合约开发者很大的自由度。我们在sdk中预先定义一些可以使用的类,开发者必须使用我们提供的类来编写合约,从而确保metadata可以从代码中被准确的提取出来。

 

b.在区块生产的过程中,我们必须确保分组所使用的时间很小,从而不会过多影响能被用于实际交易执行的时间。

 

c.除了交易的并行以外,还有其他可以并行的地方,同样可以提升执行效率。比如合约的签名验证。我们知道签名与签名验证是很费时间的计算,同时签名验证不需要什么state数据。所以如果我们能把签名验证并行执行的话,我们可以大大减少用于签名验证的时间。

 

【侧链】

 

戎朋:虽然我们用并行执行的思路加速了交易执行速度,但如果在一条链上存在多个DAPP,仍然会出现资源争用的问题,相当于多个DAPP需要分时的使用计算资源,如果一个DAPP消耗资源多势必影响其他DAPP的执行,为此我们引入了侧链的思路。

 

1.每条侧链完成单一的场景,侧链拥有独立的计算资源,侧链和侧链之间资源隔离互不影响,比如:一条侧链用来实现游戏的应用,另一个链实现保险交易应用,两者互不影响(图:主链+侧链)

 

a.侧链分为内部侧链和外部侧链

 

i.内部侧链是基于AElf通过联合挖矿的形式创建的侧链

 

ii.像比特币和以太坊这样的链也可以作为外部侧链的形式加入aelf

 

b.目前我们通过侧链相互索引可以做到:

 

i.交易的存在性验证

 

ii.交易的时序行验证

 

2.下面咱们看看如何实现这两个目的(联合挖矿)(图:展示索引与通讯)

 

a.父链将子链区块头收录到自己的区块中

 

b.子链记录父链的区块头到自己存储合约中

 

c.通过这两个步骤就可以实现相互验证

 

3.非联合挖矿

 

思路是:父链 和 子链 分别构建对方的轻节点

 

【集群】

 

戎朋:上面我们提到的并行执行和侧链,都是基于集群,aelf的每一个节点都是基于集群运行

 

1.首先看下整个集群的结构(图:集群整体结构)

 

我们通过使用Kubernetes实现对集群的管理,集群里面最大的管理单元是链,有主链和侧链;Pod是Kubernetes中的最小的管理单位,一个链中有多组不通的Pod角色组成:Launcher Pod负责启动链的节点

 

a.Akka Manager Pods是一组服务器为并行框架提供发现及管理服务

 

b. Worker Pods是一组服务器,负责并行执行

 

c.DB Pods是一组服务器,存储链上的数据

 

2.更详细一些,下面是集群中的并行执行结构

 

a.aelf是通过Akka框架实现并行执行

 

b.通过多个Worker Pod组成并行执行的集群

 

c.每一个Worker Pod由多个actor组成,actor为Akka中最小的计算单元,我们的交易就是最终分配给这些actor完成执行的

 

3.不仅并行执行通过集群管理,通过联合挖矿的侧链也属于集群的范畴,这里展示的是一个主链+2个侧链的结构

 

a.Kubernetes链和链之间使用Namspace来做资源隔离

 

c. 主链和侧链之间通过Merge Mining(联合挖矿)的形式实现相互索引

 

以上内容是aelf技术副总裁戎朋在UDC大会上提出的aelf的技术解决方案。

 

区块链技术的发展,经历了野蛮生长,也经历了变革稳重。通过戎朋此次的讲解也为我们揭示了区块链其未来发展的路径,可能对于行业外之人来说,行业已经达到冰点,但笔者认为行业才刚刚开始。

扫码下载app 最新资讯实时掌握