上篇我介绍了了阿希跨链架构的一些特点和优势,但主要是从宏观上体现了这个系统的潜力,这一篇我准备从应用开发者角度来分析下基于阿希系统做开发能得到哪些切实的好处。

第一,阿希的每个应用都由一个独立的链来承载,应用链与主链之间是松耦合的,应用链之间也是隔离的,因此这会带来以下几个个好处

  1. 应用内部的错误与缺陷不会扩散,不会影响到主链,更不会影响到其他应用,不会出现类似DAO的事件
  2. 应用于应用之间是并行的,因此不会去竞争本来就少的可怜的公有链的带宽、存储与计算资源
  3. 每个应用可以自行指定交易所需消耗的费用,一个应用甚至可以选择支持多个币种作为燃料费用,这解决了开发者与用户在成本上的后顾之忧。

第二,阿希的应用并没有采用chaincode的方式,因此是易于升级的。目前很多平台采用的chaincode的机制显然不适合开发大型、复杂的业务逻辑,合约代码一旦作为chaincode被提交到链上,就无法修改了,而稍微复杂的软件都会有漏洞和缺陷,我们也几乎不可能在第一次发布的时候就把系统设计的完美无缺,软件总是需要升级的。当然,chaincode也有其适用的场景,就是一些逻辑较简单的金融合约,考虑到这一点,以太坊提出的图灵完备的语言就是一个悖论了,图灵完备就是为了实现复杂业务逻辑,但由于他们的chaincode难以升级,于是无法(在第一次发布时完美无缺的)实现复杂业务逻辑。

第三,阿希在开发模式上,是选择了框架的方式而不是抽象出一门高级语言的方式。我们希望开发者能充分利用和组合现有的软件系统、组件和库,而不是依赖于一门尚不成熟的高级语言。开发过大型、高性能服务器程序的同学应该都知道,这种软件的主要难度不在开发语言层面,而在于架构层面。一个后端程序员拿到需求的第一时间想的问题不会是该设计哪些类或者函数,如何实现算法,他首先要考虑的问题可能是数据库选型以及数据库模型的设计。下面我会举个实际的例子,来对比下以太坊平台与阿希平台在开发模式选型上的不同的设计原则
vitalik曾在这个ppt里介绍了一个域名服务的伪代码

1
2
3
4
5
6
7
8
data domains[](owner, ip)
def register(addr):
if not self.domains[addr].owner:
self.domains[addr].owner = msg.sender

def set_ip(addr, ip):
if self.domains[addr].owner == msg.sender:
self.domains[addr].ip = ip

看上去很酷,这么简单的代码就实现了一个域名服务。但是稍微有点开发经验的同学可能会在接下来的几秒中开始怀疑了, 比如,那个domains是保存在内存中吗,程序关闭,数据会不会丢了呢?如果存在磁盘的话,那么这个语言相当于耦合了数据库组件,而且很有可能是一个k-v数据库,如果是一个kv数据库,我以后如何扩展其他字段,如何按不同的字段进行检索呢?这个数据库的容量有多少呢?还有,如果每一个变量都映射到磁盘,那么当我想真的在内存中存储数据,该如声明与定义呢?
我们看看在阿希平台上,我们是如何实现一个类似的应用的。
首先,我们需要定义一个数据模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// model/domain.js

module.exports = {
name: 'domains',
fields: [
{
name: 'address',
type: 'String',
not_null: true,
index: true
},
{
name: 'ip',
type: 'String',
},
{
name: 'owner',
type: 'String',
not_null: true,
},
{
name: 'suffix',
type: 'String',
not_null: true,
index: true
}
]
}

(注意,这里我们增加了一个suffix字段,用来查询以某个后缀结尾的所有域名)
接下来,我们需要实现核心业务逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// contract/domain.js

module.exports = {
register: (address) => {
app.sdb.create('Domain', {
address: address,
owner: this.trs.senderId,
suffix: this.address.split('.').pop()
})
},
set_ip: (address, ip) => {
app.sdb.update('Domain', {ip: ip}, {address: address})
}
}

第三步,我们要为这个服务增加一些访问接口

1
2
3
4
5
6
7
8
9
// interface/domain.js

app.route.get('/domain/:address', async function (req) {
return await app.model.Domain.findOne({address: req.params.address})
})

app.route.get('/domain/suffix/:suffix', async function (req) {
return await app.model.Domain.findAll({suffix: req.params.suffix})
})

我们可以看到,这些代码里面涉及到很多编程语言层面以外的东西,比如那个model的schema很可能是在描述一个关系数据表格,app.route.get是创建一个api handler,app.model.Domain则是一个ORM的模型接口。这些都是传统web程序员很熟悉的套路,不是吗?并且这些代码足够简单,灵活,也是非常具有扩展性的。