智能合约全栈介绍 - 钟文斌 | Jeth 第三期

686 阅读26分钟

编者按:本文系 Qtum 的 钟文斌讲师,在由掘金技术社区主办,以太坊社区基金会、以太坊爱好者与 ConsenSys 协办的 《开发者的以太坊入门指南 | Jeth 第三期 - 上海场》 活动上的分享整理。Jeth 是围绕以太坊技术开发主题的系列线下活动。每期 Jeth 会邀请以太坊开发领域的优秀技术团队和工程师在线下分享技术干货。旨在为开发者提供线下技术交流互动机会,帮助开发者成长。

欢迎添加稀土君微信:xitujun,回复“以太坊”进群和讲师交流。

本场分享视频回放链接(B 站)

分享整理传送门

1小时搞明白以太坊 DAPP 开发 - 熊丽兵 | Jeth 第三期

讲师介绍

钟文斌,本科毕业于上海交通大学,中国科学院硕士,现任 Qtum 量子链中国首席开发工程师,主要负责 Qtum Core 和 Qtum x86 虚拟机等开发,对区块链基本原理有较为深入理解。在加入 Qtum 之前就职于 SYNOPSYS 任高级研发工程师,有超过四年的大型软件开发经验。

大家好。我是钟文斌,来自 Qtum。为什么我能来讲以太坊智能合约开发?因为 Qtum 本身是支持以太坊这套虚拟机的,相当于兼容吧,所以我们也算是有一些了解。

今天我会讲以下几个话题。

首先,为什么我们要智能合约?智能合约到底长什么样?最后我们说以太坊智能合约全栈走一回。DApp 作为一个 APP,在用户点击那个按键之后,它到底发生了什么?我们今天都会涉及到。

为什么要智能合约

智能合约大家应该知道,不论是比特币以太坊还是很多现在的需要挖矿的链,他们都在进行挖矿这么一个行为。那挖矿是什么?我今天就不做深入解释了,但是至少大家知道挖矿是非常浪费资源的。

那这么浪费资源到底是为了什么呢?

为什么我们好好的跑在服务器上的 app,需要变成 DApp?它其实是一个社会科技。整个社会之所以存在,是因为人与人之间要协作。在不存在协作的情况下,可能是我自己做一个独立开发者,可以做任何事情。当我需要跟我的朋友或者合作伙伴去合作的时候,我就需要互相信任。这些信任是需要成本的,合作的范围越大信任的成本就越高。比如说我和我的亲朋好友合作,那基本上我是非常信任他的。再往大说,我们要在整个社区合作,比如说整个掘金社区要一起做一件事情,我们可能就需要更大的信任。

这个信任最重要的一点在哪里?就是不要侵犯别人的利益,并且保障自己的利益。熙熙攘攘皆为利益往来,所以没有利益的合作,就不大需要信任,但是最需要信任的地方是哪里?往往就是有利益的地方,有钱的地方。所以无论是社区城市国家,甚至全球都需要这么一种协作。要实现协作就会有成本。

区块链能做什么?降低互相伤害的风险,那友谊的小船就不会翻,所以说它就是使能够使人们能够达成信任,也就是说能够在利益上达成一致,大家可以不用担心别人骗我的钱。

我刚才说,这是一个Social Technology,就是说之前我们所有人都在基于信任做事情,但实际上信任的门槛是非常高的。刚刚大家都已经互相认识了,但是我想问一下你信任对方吗?这是非常难的。但是你信任你的父母吗?你应该是信任你父母的,这是在长时间的积累下,才有这样一种信任。

所以信任是很难建立的。人们想到法律,用法律来保障人之间的信任,但这远远不够。还有就是我们一开始是亲友的一些人情,到后面变成市场。我们一开始的协作可能是我们两个人,最后演变成更多的人参与,就需要更健全的法律。或许我们可以用智能合约来实现这一切。

这个是之前经合组织发布的,一些国家的政府开销占整个 GDP 的比重。

我们可以看到这36% 到58% 不等,这是个非常高的一个比例。这些比例在做什么?就是使大家能够协作。也就是说让大家能够信任政府,或者信任这些组织,能够做一些事情。这就是信任的成本。所以智能合约就是,我们用程序用代码来代替我们人工实现这些信任,让智能合约作为一个信任和协作的工具

以上就是简单地介绍了一下智能合约做什么,为什么我们需要去挖矿?它其实就是说能够让人建立起信任。

智能合约具体是什么

它说是一种合约,实际上就是一段程序。大家应该知道 AWS LAMBDA。一般我们去开发一个业务逻辑,在开发业务逻辑的同时,我们还需要去维护后台的一些服务器,去维持这个业务逻辑。AWS LAMBDA 可以实现这么一个功能,你只需要关注你的业务逻辑,然后你把业务逻辑放到 AWS LAMBDA 这个平台上,之后,用户只要调用这个业务逻辑,他就可以用了。你不用去管这个服务器在哪运行或怎样,最后就是用户用了多少计算能力,他就付多少费。

类似就是这么一个架构。这个其实跟区块链的理念有那么一点像。区块链或者说以太坊,也是这样的一个平台。你只需要关心你智能合约写了什么,但最后它跑在区块链上,你不需要去关心,但每个用户需要为调用这个智能合约付费。

举例一:计数器

这就是一个智能合约,是一个计数器,大家应该都能看懂。

第一行,是 Solidity 的版本号。Solidity是现在智能合约最主流的一个编程语言。这合约的名字叫 simple counter,就是一个简单的计数器。它很简单,里面有个 counter,有个 function,就是对这个 counter 进行一个加加的操作,好像跟普通的 JS 的程序没什么太大区别。 但实际上智能合约和它有很大的区别。

它在操作的这个 counter,实际上是在操作一个数据库。就是说如果是一个类的一个实例,一般是放在内存里面,内存清掉之后,这个 counter 的所有数据都不存在。实际上在这个合约上面,这个 count 是记录在数据库里面的,所以你每进行一次加加,实际上是进行了一次读写,对数据库进行了一次读写。它和普通的 JS 稍微有点不一样。

举例二:功德香火链

我们再看一个更好的例子。这个例子是我个人非常喜欢的。

这个叫做功德香火链。这个的需求其实很简单,就是说你捐赠香火,我们在区块链上给你计一次功德,给你念一句阿弥陀佛。大家感受一下这佛光。

其实这个例子我们简化一下,它有三个需求。

首先我们要初始化,设定这个链上有一个管理员,比如说方丈,方丈管钱。每个人都应该能够捐赠功德。所以每个人都要能够捐赠,并且这部分钱只有方丈可以动,不是每个人都可以取出来的。所以说如果你在刚才说的 AWS LAMBDA 上面去做这样一个东西,是没有人会信任你的。你可以随意改这里面的数据,你也不知道我有没有记你的功德,这钱用在哪你也不清楚。但是智能合约它最厉害的地方就是说它跟钱相关,并且能够让别人相信这个钱没有被乱用,这是怎么做到的呢?待会我们讲全栈的时候会解释,但现在我们先来看看这个合约。

这个合约通过刚才需求分析,其实就这么十几行。我们稍微把它分解来讲一讲。首先第一个需求,我们要设定管理员。 这个就是相当于合约的初始化的一个函数。

这边有两个比较关键的地方,一个就是 msg.sender,相当于当前调用那个合约的用户。你在区块链上放一个合约,每个人都可以建造一个这样的链,所以你怎么知道这个链是不是某个大师发布的呢?那这个大师要公布一下这个他所创建这个合约的地址。每个人都可以创建一个不同地址的合约,但是只有这个大师,他公布的才是大家想要捐赠功德的地方。所以这个 msg.sender 就是当前合约创建者的地址。我们把创建这个合约的这个人设为 owner,就是管理员。

第二个需求就是每个人都必须能够捐赠。

有几个比较关键地方,这边有个属性叫 payable,就是说这个合约是可以往里面打钱的。 我刚才说过智能合约最厉害的地方就是它可以管钱,所以你可以通过设置 payable 的属性,往这个 function 里面打以太坊。这个 msg.sender 就跟刚才不一样了,他当然还是调用合约的这个人,但现在是调用合约的人就变成了捐赠者,他往里面捐钱,然后他会做一个 assert,说你捐赠的金额必须大于0.001以太,不然就不够香火钱。你敬赠完之后,如果满足条件,那我就给你念一下阿弥陀佛,然后给你记录一条日志,大家都可以在链上看到你捐了钱,你被念了一句阿弥陀佛。

最后一个需求是说只有方丈可以取钱。

这边有一个 require,当前调用 withdraw 这个 function 的人必须是管理员。我们一开始创建合约的时候就创建了管理员,就是这个方丈。一旦认证通过,方丈就可以取这么多的钱到某个账户。这边还可以再插入一个日志,比如说去翻新佛像,类似这样的事情。这就是一个非常简单的智能合约的例子,它与普通的 APP 不同之处就在于它所有东西都记录在区块链上,它是公开透明的,大家可以验证这个合约确实存在。而且你可以看到往里面打的每一笔钱,任何人都没有办法修改,包括这个 owner,他只能提款,他也不能修改里面发生的所有转账的行为。

EVM 平台的特性

我们还回到这个智能合约这个东西。我们刚才讲到都是跑在以太坊上的智能合约,以太坊的虚拟机就是智能合约的执行环境,叫 EVM,Ethereum Virtual Machine。它有一些什么特性呢?

一是调用合约由用户承担计算费用。 无论是方丈创造那个合约,还是你去捐赠,还是方丈去取钱,它都需要一个叫做 gas 的手续费,这个手续费都是调用合约的人去付。

二就是用户能够完全掌握这个权限。 你的捐赠行为和方丈的提现行为,都由他们自己控制,通过什么控制?通过客户端的一个签名,你掌握了私钥,你就可以对它进行签名,签名之后服务端去验证,通过之后再在整个区块链网络上去广播,再去验证。所以说每个人它自己控制了自己所有的权限,不存在说像AWS lambda 一样,你挣的钱就相当于给了这个平台。但是这个是放在一个合约里面,大家都能看到的,都能验证的一个环境里面。

另外一点就是性能低下。 以太坊网络其实全球有将近2万个节点,那比特币网络可能有将近1万多个全节点。一个2万多台机器的网络,可能计算功能应该是特别强大的,但实际上并不是。它的性能取决于网络上性能最差的那台计算机,为什么?因为每个节点都要重复计算。你对一个合约的调用,每个节点都需要计算一遍,每个节点都相当于存储了一份完全的拷贝,它需要去达成全网的一个共识。这就是为什么我能够信任这个网络,因为即使少数的人在作弊,我们大多数人都还在验证大多数人所承认的事实。所以我们可以相信比特币或者以太坊这个网络。但是这它的性能其实是很低下的,因为每个节点在做的事情其实是一模一样。

去中心化平台的缺点

,刚才已经介绍过。

,每个调用合约的行为,你都需要付 gas。

,就是它性能也很差。

我们访问一个网页,相当于发出一个网络请求,服务器就把所有信息发给我们,这个请求非常简单,这个整个交互的过程非常简单,生命周期非常短。但是在以太坊上在比特币上所有的交互都是通过 transaction,交易来完成的。整个交易生命周期非常复杂,还需要经过所有节点的全网验证,而且中间会出现很多各种各样的错误。而且它也没有这种网络请求,也没有可靠的随机源。

最令人发指的一点就是合约一旦部署了是没法更新的。你可以删除某个合约,但是你不能更新。一旦出了 bug,问题会非常严重。普通的软件出了 bug,可能就是一个运营事故。但是以太坊的 bug 出现过几次,都是动辄几千万美金。比如说 Parity 那个事件,它里面的一些合约被删掉,那个钱虽然理论上说没有丢,但是它所在的某个地址上,所有人都没有办法再把这钱取出来,1.5 亿美金就被冻结了。所以说智能合约上的一个小小的瑕疵,都可以造成非常大的金融损失。但这也是它的魅力所在,它能够管钱,它跟钱相关。人之间的共识信任很多是建立在这种利益之上。

去中心化意义何在

按照套路,我现在就要讲它意义何在了,实际上我也还没想清楚。所以说这是抛出来一个开放性的问题。

我刚才已经介绍了去中心化平台大概是什么样子,它的特点是什么,但它的意义何在呢?我不想去评论一些什么,因为整个行业都还在思考。去年的我如果在这个时间点说,我会说它可能会要改变世界,但是今年一年的事情发生下来,去中心化应用这条路,到底最后能不能走通,我个人目前保持观望态度。所以课程我们还继续讲,但是意义何在,希望大家听完之后自己去思考。我们这个世界到底到底需不需要所有东西都是去中心化? 我认为肯定不是,我认为可能是一个中间状态,但去中心化一定有它的意义所在,至少我们现在拥有比特币可以不需要一个中心化的机构实现一个全球的那么一个价值流转。至少我们有以太坊,它使我们能够在大家互相不认识的情况下,进行 ICO,进行融资,这是一个非常伟大的创举。

DAPP 全栈走一回

我们刚才说到了智能合约,举了两个例子,那如果它变成一个应用,它最后在链上到底发生了什么呢?我们再举一个例子。

举例三:简单存储合约

这个是一个非常简单的智能合约,就是一个简单的 set 跟 get ,一个简单的存储,并且获得值的这么一个应用。

一个应用,它本身可能从前端到后端有这么一些东西,所以我待会也会从前端到它怎么进行一个 RPC 调用,怎么样进行 ABI 的编码,合约怎么样运行。然后在 EVM 上是变成怎么样运行的,最后是怎么样放到那个数据库里面。所以这一点从这个一路我都会讲下来。

前端:WEB 2.0 VS WEB 3.0

我们先来看看,这是 Howard 同学写的,关于web 2.0 和 web 3.0。

Web 2.0就是现在大家所熟知 Web 的整个框架。那类比于 DApp 的话,我们暂时把它称为 Web 3.0。

前端 Web 里面可能是 HTML 服务,那在 DApp 开发里面,我们可能就是一个 DApp 或者 Web 3.0,其实没有太大区别。

那刚才说了 Web 2.0 可能是网络请求,但在 Web 3.0 里面所有东西都是通过合约,通过 transaction 交易去触发的。

Web 2.0 里面数据的组织方式可能是 JSON,在以太坊上可能是 ABI 编码

服务器端我们可以通过这些请求,我们可以进行一些操作,或者实现一些业务逻辑,那在所谓的 Web 3.0 里面,它其实就是通过智能合约来处理这些业务逻辑。

在 Web 2.0 里面我们可能会有操作系统,Web 3.0 都是以太坊虚拟机。实际上它是把虚拟机跟操作系统做了一个混合,区分的不是非常的明确,既有虚拟机要做的一些事情,也有操作系统层面的一些事情。

那在 Web 2.0 里面,我们可能有数据库,在 DApp 开发里面也有数据库,是通过 Merkle Tree 的方式去组织起来的。实际上在以太坊里面合约存储并不是 Merkle Tree,是 MPT,Merkle Patricia Tree。我们可以把它简化成 Merkle Tree,其实指导思想是一样的。

所以前端其实要做的事情很简单,就是展示链上数据,或者是 DApp 的一个入口。

我们在本地需要做的事情就是签名,所有东西都是一笔交易,你可以在这一笔交易里面发送以太坊,也可以在这笔交易里面去调用合约。

我们平时可能接触到跟区块链比较相关的一些应用,那大概分为这两类。就是像 coinbase,binance 那些交易所。这个平台实际上并不是一个 DApp,虽然它跟区块链相关,处理区块链的一些交易,但实际上它是一个完全中心化的平台,所有的数据都在他的服务器上面,所有的请求,甚至你的币都是打给他们平台。

另外就是 myetherwallet,imtoken 这种手机端的或者网页端的钱包软件,每个用户自己能够完全掌握私钥,不用把钱把以太坊比特币打给任何一个中心机构,用户完全掌握这笔钱的动向,所以它更像一个去中心化的应用。

前端例子:REMIX IDE

我们看看这个前端的例子,刚才那个合约大家还记得吗?一个set 一个 get。这个REMIX IDE 上面有一个叫 simple contract 的合约。这合约有两个 function,一个叫get,一个叫set。我们现在 set 一个666,然后你按下这个按键之后发生了什么呢?

用 RPC 递交事务

它会生成一笔以太坊的交易。比较关键有两个数据,一个是 from,说从哪个地址调用,一个是 data ,你调用的这个合约里面包含了哪些数据。你按完这个按键之后,你会发现你在以太坊上进行了这么一笔交易。

这个交易 function 是 set,它 set 了一个16进制的值,就是666。这个就是以太坊上一个交易。

用 ABI 编码传递数据

我们来看一下它传递的参数,我们刚才看到一大串那个data,它是什么呢?这个 data 的前四个字节,实际上就是选择调用哪一个 function。这四个字节是怎么来的?

对 set 这个函数的原型做一个 SHA 的操作,它会变成很长的一串哈希。我们取前面四个字节作为这个函数的标识。所以你看到 data 的前四个字节就能确定,我现在调用的是这个 set 函数。后面的这个数据就是我们要传入的参数,就是666。具体到以太坊的虚拟机怎么处理这么一段东西呢?

EVM 字节码

它可以变成这样一个字节码。最关键的一点是最后它会进行一个 sstore 的操作,它会在某个数据库里面把这东西写进去。我们把它变成比较好理解一点的伪代码的话就大概这样。

它其实进行了三个东西,头四个字节,我可以确定我是要跳到哪一个函数,并且这个函数传入的参数是第四个字节后的那32个字节,它每个参数的长度都是固定的32字节。在666读出来之后,这个 set 函数进行的操作就是往一个内存里面写这个值。

数据存储

那写在哪里?它实际上是写在一个数据库里面。但是这个数据库会通过 Merkle Tree 的数据结构,反映到区块上面。Merkle Tree 是个二叉树,它所有的节点的值都是下面两个子节点的哈希。所以一旦发生某一个值的改变,整个树相关路径上的哈希都会发生改变,最后的根也会发生改变。这整个树的根的哈希相当于所有这些数据的签名,或者标识。一旦某个数据发生了任何改变,整个 root 哈希也会发生改变。刚才我们把 666 这个数据写到一个地方,然后这个整个哈希就发生改变,我们重新计算出一个根哈希的值。这个值就放在区块链上。区块链它每个区块除了包含所有的交易之外,最重要的一块,它有一个区块头。这个区块头有几个结构,一个是它包含上一个区块的一个哈希值。以此类推,我这个哈希值标识了前面所有的区块所有的交易。如果别人想骗我,说他改变了前面的某一笔交易,那我这个哈希的验证就不会通过,它会被整个区块链网络认出来,说这个是篡改的。我们之所以说区块链是不可篡改的,就是因为有这样一个结构。

那另外一个东西就是我们刚才说到 Merkle Tree 的根。Merkle Tree 的根就是所有交易的哈希值的一个标识,一旦这些标识被确认之后,这个根也就唯一确定了。然后这边有个 nounce,就是我们所谓的挖矿的关键。所谓的挖矿,其实是在计算符合某一个难度要求的哈希值。为什么比特币要去挖矿,首先比特币想实现一个去中心化的网络。在中心化的网络里面,我需要服从一个中心化组织。但是在去中心化的网络里面那我听谁的?我产生一个一个新的区块,我应该服从谁挖出来的那个区块呢?那就让大家一起算一个值,这个值是一个哈希值,它没有任何取巧的办法,只能通过增加算力来解决这个问题。把前一个区块的哈希加上这个 Merkle Tree 的这个根,再加上区块头的一些信息,这个 nounce 是唯一可以改变的变量,你通过改变 nounce 从 0 到 一个非常非常大的值,你可以一直在算某个哈希。一旦这个哈希符合某一个难度要求,比如说前面 10 位为零或者 11 位为零,它就相当于满足了全网共识的难度要求。在这个难度要求下,你产生的这个区块就是合法的,你就可以向全网广播。一旦你是全网第一个算出这个哈希值的人,你就成为最新的这个区块生产者,可以获得比特币或者以太坊的奖励,也就是说你挖到矿了。比特币或者以太坊,可以通过这样的方式实现比较分散的分发(假设矿池不存在)。还有就是可以保证随机性。在一个去中心化的、大家谁都不信谁在网络里面,如果某一个人固定去产生区块,他很快就会被攻击比如 DDoS 之类。他一旦不能产生新的区块,整个网络就会瘫痪。通过这个方式可以非常随机的让所有人都有机会生产下一个区块,攻击者完全无法预测下一个产生区块的是谁,那他就无法去攻破这个网络。 所以说我认为有两点,一就是币的分发,二就是保证随机性。 我们刚才说到数据库,保存这个数据的 transaction,最后反映到这个 Merkle Tree 上,最终通过一个哈希的方式存在链上。随着这个链越来越长,相当于每个区块都有前面所有链条的证明,它就变成了全网共识,没有人能够改变,也没有人能够去伪造。 这就是为什么我们刚才 set 了一个666,它不会存在某一台服务器上,或者是几台服务器上,或者某个人能够去更改这个666。因为全球一万多个以太坊的节点都在他们本地的数据库上记录了,我这个合约里面这个值就是六六。即使你某几个人想去改他,也不可以打破这个共识。包括刚才那个捐赠功德香火链也一样,所有人都知道了,你这位仁兄是念了五句阿弥陀佛的,这个方丈是取了这笔钱来干嘛的。他不会由于任何个人或者少数人的作弊发生任何的改变。

刚才这个过程就是我们从前端到ip到最后的存储,看了一遍,以太坊上智能合约是怎么样去运行的,它最后到底产生了一个怎样的效果。

新一代智能合约平台

我们刚才讲到都是围绕以太坊,但是不得不说以太坊的虚拟机并不是一个特别好的一个虚拟机,当然它是为了智能合约专门设计的一个未来安全性实验的一个虚拟机。目前以太坊虚拟机还有以下缺点:

  • 只能用 Soidity 语言编程
  • 没有标准库
  • 合约之间的互相调用关系无法预先确定,无法并行执行一些合约
  • REMIX IDE 编译器过于古老
  • 虚拟机与操作系统不分层
  • 智能合约无法更新

业界有很多人在做新的虚拟机,比如说比较火的 WebAssembly、EOS 他们都在做。可以支持 JS 网络编程。然后比如说 RChain 这个项目,他自己搞了一个 Rholang 就是新的编程语言。 我们 Qtum 也在做新的虚拟机,是一个 x86 虚拟机。相信大家应该都听过 x86 指令集。基本上我们现在所有的设备至少是非移动设备,还是用英特尔的 CPU,他支持的就是这个指令集。大部分编程语言,大部分的工具都能够被编译成 x86 指令集的可执行目标文件。也就是说如果我们在区块链上支持了这个虚拟机,那目前大部分的主流语言和主流的大家熟悉的工具,就可以直接被拿来使用来编写这个智能合约。所以说可能这个可以给大家带来一些便利,我们也希望如此。

还有就是说,以太坊的虚拟机跟操作系统层其实是混合在一起的,这在编程当中可能带来了很多的困扰,所以说我们会把它进行一个分层的设计。另外整个智能合约它是没法更新的,但是在 Qtum 里面我们可以做到这一点。就是我们有一个相当于分布式自治协议,那通过这个协议我们可以但比如说我们有一些标准库。那整个业界,当然我这边没有列全,也各种各样的项目都在做这个虚拟机。但不可否认目前这个以太坊还是最流行或者是最成功的一套虚拟机,也是最成功的一个智能合约运行环境。

我今天分享就到这里。这是我们GitHub地址:qtumproject,供大家参考。