智能合约全栈介绍 - Howard | Jeth 第一期

2,327 阅读22分钟

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

Howard 老师本次分享视频回放(B站

分享整理传送门

以太坊智能合约 + DApp 从入门到上线:来自前端工程师的实战指南 - 王仕军 | Jeth 第一期

详解 ERC20 代币及众筹 - 熊丽兵 | Jeth 第一期

Howard 是《Deep Dive Into Ethereum Virtual Machine》一书的作者,该书深度剖析了 Solidity 和以太坊的原理。目前,Howard 任 Qtum 量子链 DApp 平台核心工程师,负责开发工具和数据库。Howard 在创业界拥有10年的产品开发经验,并且对构建去中心化产品充满热情。他也是本次活动的出品人。

大家好,很荣幸今天跟大家分享一下以太坊智能合约的一些开发经验,在开始之前,我今天先给大家先讲一个高层面的介绍,从前端到后端介绍一下以太坊的技术栈;然后之后我们两位老师会给大家去介绍一些细节。所以我的这场分享只需要大家脑海里有个对以太坊的概念即可,具体细节后面我们两位老师会给大家更精彩的讲解。

我今天就先介绍一下智能合约以及它存在的理由,然后带大家看一下智能合约长什么样子,最后会带大家梳理以太坊的整个架构,从前面面对用户的DApp到后面的存储数据库。

先简单自我介绍一下,我是台湾同胞,目前在大理远程办公。

为什么要智能合约

切入主题,为什么要有智能合约,挖矿非常的费资源,为什么要花这么多的精力这么多的资源在挖矿上面?

有位区块链先驱者提了这样一个概念:技术让人跟人之间去协作。我们个人自己跟自己协作,但是我可能多做一点事情,会找我的朋友找我的亲人去合作,以此让我做更大的事情,这样我们需要一些合作,我信任我的兄弟,我信任我的同学这种类型的合作。更多的合作我们跟以太坊的爱好者我们掘金的事情我们互相的爱好及把我们捆绑在一起让我们做更大的事情,再更大一点在一个城市,这个时候我们要有法律,我们需要各种政府部门,机关,就算我不认识你,我不认识买我产品的人,但我可以通过我们的合约去保证我们合作的可行性,更大一点就是国家跟国家、国际法院的合作等,这些跨边界的合作是更复杂的体系。人类的文明跟技术是随着这个合作的复杂性而增长的。

那为什么我们要有规则呢,信任其实是非常昂贵的资源,我最信任的人就是我的亲人和我的朋友,尤其是从小一起长大的朋友;但是我来到城市、国家这个级别的话,其实是很难信任一个不认识的人,所以我们要提高达成合作的可能性,就要降低信任最小的需求。我们只要通过这个概念降低互相伤害的风险,友谊的小船就不会翻。

我们要尽量的信任别人,他讲这些东西是科技,我们从人跟人之间的信任上升到法律,所以我们开始讲法律时就不需要提及信任。从人情到市场,我可能愿意跟人合作,到市场我们开始讲价格,我们有价格的协调机制,就不需要讲人情了。我们点对点的对话,必须要认识你这个人我才有办法跟你协作,多对多的撮合市场机制,这是一个持续演化的过程。

所以我们开始讲这些事情的时候,会牵扯到社会扩容,社会的扩容是需要成本的,我们需要有政府,需要有法院,需要有警察机构。上图中我们可以看到OECD政府的GDP占比是从36%到58%不等。我们虽然没有买单、没有直接的去支付这些成本,但是实际上这些东西在政府开销上占了很大比重。那区块链做的事情他是因为取代信任的机制,即以计算机替代人工成为核心的概念。所以说智能合约他具体就是一个计算机程序,它来替代靠人工运营的这些机构。

智能合约具体是什么

接下来我们可看一下智能合约具体是什么东西。第一个例子就是亚马逊的 LAMBDA 服务,LAMBDA一个特性是你在LAMBDA这个平台写程序的时候,你只是去写你的业务逻辑,而这个业务逻辑你直接上传到 LAMBDA 平台上面,好像只是一个服务器的回调。只要有请求进来它就调用这个回调,计算完成后就直接终止了,你要自己去部署一个服务器,没有逻辑。LAMBDA 这个平台让你直接把服务器的这个业务逻辑放在平台上面,完全不用去管后面对应哪些具体服务器,一有请求进来,并把 LAMBDA 启动,这个跟智能合约的架构非常像。

我们可以看一个较为简单的例子,这个计数器的业务逻辑很简单,这边有一个COUNT,把它想成数据库里的数据,这个合约有数据也有业务逻辑,这个业务逻辑要做的事情就是把这个数字递增。当外部调用这个递增的时候,它去数据库里面修改这个COUNT这个变量,这个就是你的智能合约,从概念上来讲也没有什么,它之所以牛逼是因为我们用去中心化的网络去同意了这个数据应该是什么,我们具体接下来看一个更好的例子。

我们把这个合约的这些变量理解成一个数据库,我们可能平常在写一个加法的类型,它只是存在于类型里面,你的假若死掉了,这个内存就清空了。在智能合约里面,虽然它是变量,但是你是写在链上面,而这个数据库恰巧是特别昂贵的数据库。我们类比成 Java 的伪代码,我们会想象后面每一个合约它有一个相对应的数据存储,它是一个对象,是一个数据库,在这个方法里面刚才讲了递增的方法,它只是去操作这个数据库,在这个键修改它里面的值,所以你每次去改一个变量其实是对数据库做一个操作,而且这个操作是特别昂贵的操作,可以给一个概念,普通所有的这些指定是要费用的,普通的一个纸币,比如说加减乘除,大概是2到5个不等,存储的话是2万个,大概算是5000倍的倍增。

我们看一个更完整的例子,功德香火链,这个例子大概介绍了智能合约非常本质的一个东西。为什么要写智能合约,我们并不是只是为了写这个数据,他之所以牛逼是因为他里面有金融属性,大家可以相信你,如果在我自己的亚马逊 LAMBDA 上搞一个功德香火链是没有人相信的,我在数据库里面可以随便修改,但如果我们改成在智能合约中就变成是一个去中心化、不需要信任的一个程序。

大家来看一下这个东西,有一些需求,他初始化的时候我们要设定管理员,任何人我们都允许他捐款,最后要用这个款项的时候我们允许管理员取款,然后让他写出所有取款他的去处去了哪里。

这个就是我们整个智能合约的代码,就十几行而已,这上面大概有两点需要留意,有合约属性,拥有者的姓名,我们会用这个属性来存储他的管理员,我们控制了管理员权限才可以进行修改。下方分别是捐款和取款的函数,我们接下来把这两个函数分开讲解。

初始化设定管理员,所以说这边我们是要把数据存起来,我们要知道谁是管理员,在创建这个合约的时候它是用一笔交易创建的,即有一个钱包去付钱然后去说这笔交易谁创建的。这里的属性指出当前用户的身份,这一笔交易的操作者以及合约的操作者。我们这边做的事情很简单,在创建这个合约的时候我们把当前的操作者存储在这个数据库里面,有了这个东西之后我们就可以验证正在操作的人是不是管理员。

接下来我们开始做这个核心的东西,即任何人都可以转款,之后你要做众筹让别人给你转钱,这个东西就涉及到金融的属性了。我们看到这个函数也是一样,在操作这个把钱的用户,我们要记下来,也就是要记下这个功德;PAYABLE 它只是一般拿来处理跟金钱相关的东西,这里是处理存进来的钱。这里要有个断言,要确认这笔钱必须要大于0.001,因为我不想要太少,太少就看不上、不想要;最后这边有一个日志,说我们把这个输出和这个功德记下来,然后保佑发送这个钱的人,这个日志就记在链子上面了。报错是直接退款,他没有满足这一个条件这笔交易就失败了,他捐了钱就打回原本的帐号,当然这个真实的合约你会做各种其他的需求,理念上就是你先检查满足了你的需求之后,你再去操作你的业务逻辑,然后最后你就可以输出记录这个事情。我认为比较有意思的事情,这个合约他有一个相对应的帐号,这个帐号合约有多少钱,你看里面的业务逻辑并没有显性的说把这个钱给这个合约,你这个隐性只是说这一笔交易进来了,只喜欢这个逻辑,然后最后就会给这个帐号,但是你不需要代码直接说这个事。

接下来看看看管理员取款,我们这个函数可以让管理员指出取钱的数量,这里又有一个断言,我们在断言里面判断当前操作这一笔交易的人是否是这个合约的拥有者,我们一开始就要把这个拥有者记录下来,之后的操作中我们再去验证一下他是否是当初创建合约的人,如果是的话我就给这个用户给他打钱,转给他所要的钱。我们首先要做一个小测试,假如说这个帐号当前有10个以太坊币,然后拥有者试着去取11个以太坊会发生什么事、触发什么条件会报错。假如说它试着转钱,这边是从合约里面把钱转给A,它账户里面的钱不够多就会报错,同事状态会直接全部回到原来的状态。这个回滚的概念特别的重要,一般你在写程序做一个操作的时候就有一个请求进来了,你对数据库做一个操作,那你报错了,之前的数据库要回滚;以太坊会自动做这个回滚的事情,只要一失败它就马上回滚。

我们可以看一下一些平台的特性,过去通常是你自己公司买这个服务器,大家用这个服务是不用钱的;现在是用户承担所有的计算费用,GAS模型去计算,然后很核心的点是用户他掌握了权限,比如说你用支付宝想转帐你在支付宝平台上确认这笔帐能不能转,那现在用客户端在客户端做了签名,然后服务端说让你转帐,他是不会去验证这个客户在终端到底是谁。还有一点,虽然说以太坊或者量子,有着几千几万的节点,其中以太坊是有17000多个节点,看似这17000个节点计算能力还不错,事实上并没有。因为每一个节点要重复每一个计算,计算每一个数据,所以当我们说存储数据库,如果是当成传统数据库来用是不可行的,因为太贵了。

去中心化平台也是有他自己的缺点,他很慢,处理一笔交易要花20秒;其次贵,每一笔交易要花大概0.1或者是0.2美元,如果你要发一笔比较大的合约就是10美元到100块人民币左右了。你做应用交易的生命周期极其复杂,这个东西你必须要等他确认,等一次两次三次,然后他可能中间会抛错,各种不同抛错情况,有可能是业务逻辑抛错,也有可能是他给的GAS不够,然后跑一半逻辑他没有钱了,就终止了,还有一些其他的在业务上有一些没有办法做,没有网络请求没有随机员,还有你不能更新合约代码,之前出了一些漏洞,有些人写了合约,这个合约被别人删了,然后他很多钱就卡在里面,几千万美金的钱卡在里面。所以说在做的时候就可能需要思考一下我这个东西去中心化有意义吗,到底是哪些东西值得去中心化。

DAPP全栈走一回

那我们接下来更深入的看一下,DApp全栈走一回,我们用一个简单的例子然后走到最后面的区块链上面存储的数据。这个合约中,我有一个变量,有一个值,然后我唯一要做的事情就是修改这个值或者是读取这个值,这个就是我的一个简单的智能合约。

我们会从前端开始,我们看一下从前端再递增RPC,这个是ABI的编码,我们一会儿再展开来说,这个合约就是他的业务逻辑,EVM最后到区块链存储,我们一个一个展开来再说。

所以说你可以比较一下WEB 2.0到3.0他们技术站的对比,然后WEB 3.0就在吹牛说WEB3.0技术是一样的,只是换汤不换药,在WEB 2.0我们做了一个请求,用户按一个钮就产生一个请求,用户按一个钮就产生了一笔交易,会带有一定的金额,这个需要整个网络去确认,而不是单一的服务器去确认,但是在前端是由用户批准交易之后再丢给网络让网络去处理。然后你写后端说我这个请求他里面具体的数据是怎么样的给一些结构,那我们现在做后台的话常常是用ABI的结构,以太坊这边它相对应的这个数据结构就是叫做ABI编码,传给智能合约的数据就是用ABI编码,在WEB 2.0我们做服务,后来在WEB 3.0我们想写智能合约去做这些服务。但操作系统层面在WEB 2.0时是 Windows 这些操作系统,现在的以太坊就是虚拟机,同时也能做一些操作系统方面的处理,比如说存储数据转钱,或者是调用其他合约,这些是用EBM来做的,这些是混合了两个不同的东西在一起,又是虚拟机又是操作系统。最后我们要存储数据,就是在WEB 2.0文件系统这些去存数据,在WEB 3.0我们有电子版本控制,你可以有各个版本。

那我们看看前端,前端要做的事情就是展现链上的数据,重点还是一样,用户在本地签名之后再把数据提交到链上,这个权利是握在用户的手上,我们可以比较一下WEB服务和纯客户端的两个概念,大家熟悉这个应用就是币安这种平台做交易,实际上这些不够形成这些权限的控制都在平台手上,像钱包这种东西才是比较正儿八经去中心化,你说了话不算,是用户控制权限。在这两个极端去纠结如果我真的要做一个去中心化应用的话,它的用户体验会很差很难用;好处是中心化的服务,用户体验好且反应快,但是就是出了事的话那大家都一锅掀了,现在是在两个极端里面去纠结怎么做。

我们看一下前端的例子,这个REMIX IDE,他指向合约,他有两个按钮,一个是GET 的按纽,还有一个是SET的按钮,我要通过调用的方法去改那个值。然后当我按向那个按钮我要去设定666,这个时候他用RPC去递交一个事物,然后在以太坊就是这个RPC的方法,他们的概念是一样的。

所以我调用这个方法从我的钱包去打这个数据,然后我们之后会把这个数据去展开,它也是一个RPC,这里面的数据传给智能合约的数据,我们来展开看一下。

我把刚刚那个东西调用到网络上面,我就可以看到这一笔交易,这个就会把交易里面的一些信息显示出来,我花了多少 Gas,然后里面传了一些数据。

我们看 ABI 的编码,我们可以拆开两部分,这个 60fe47b1 他也是调用方法,这个是他的方法名然后拼接在一起去取这个前面四个字节就是这个方法的选择器,我调调用智能合约的时候前面四个字节指定我要调用哪些方法,后面那些就是我传参,就是用这个编码出来的,虽然666其实只需要2个字节,但是他ABI编码的设计必须要32个字节,所以说这里可以看到他是有一定的浪费的。刚刚传到数据上来之后EVM的节点就会把智能合约的代码加在这个里面去执行,这个是我们刚刚设定的方法,传值进去进行修改。

我们可以看一下EVM的字节码,刚刚看到这个是智能合约,这个智能合约最后通过编译器会编译到字节码,他基本上是从上到下一个一个执行的。我们要关注的是有两个可以关注的,上面这边60fe47b1这个就是我们选择器,我先判断一下是不是这个方法,是这个方法的话我跳到这边来去继续执行。所以说这个就是我们SED方法,最下面我们存储数据了,这点是一个关键点,我们刚刚是要存到value这个属性,这个属性他可以理解为建值,他直接是映射到0的位置,我用0去存储,最终把这个数据存储到内存里面去,所以说这是整个合约最关键的指令,存储数据。

我们把刚刚说的那些东西从EVM字节码变成代码,这个是我发的时候他里面会带着一些数据,这个数据就是有一个指令,然后我取前面四个字节这个就是我要选择的方法,如果匹配了这个字节我就跳到tag这边,这边我要取参数,我也是从这里去取,我直接从四个字节以后取32个字节出来,这个就是我的参数,然后最后我把我读出来的参数存到这个位置,这个就是我们刚刚合约做的事情。

我们下面分析一下sstore这个指令是在干啥,我们刚刚做的事情就是把数据存在第一个位置,L1这个位置,这个位置可以是无限多的,我存在这个位置之后他就会更改,其实最上面的这个就是我的版本号,我的版本号他是取这两边的,然后每一个节点都一样,这边的 hash 是由下面来更改的,如果我更改了这里面的数据,这边的 hash 会变,然后导致最上面的这个版本号也变了,所以说直接把这个 hash 当成版本号就可以了。如果我去改动这边的数据那通过这个路径去改变最上面的版本,下面这边是我存储数据的地方,我任何地方存储的数据都会影响到最上面的版本号,跟 top 的版本控制有点像。

这边就是我们存储数据的数据结构,接下来我们看一下这个数据结构最后是如何融入到链里面去,这个就是咱们的区块链,里面会存着版本号,像说你去 GitHub 上面看一个项目,会有历史更新文档,会讲这个版本号修改了什么,这个版本号修改了什么。这个就数据结构类似于一个版本,每个版本会去记录这一个相对应的版本号是什么,然后从这个版本号可以取出相对版本所有的数据。

再回顾一下刚才提到的,我们从前端通过RPC,这个是以太坊节点提供的服务,通过到RPC调用这个节点,调用里面的数据是由ABI编码结构化,然后传给合约,合约处理这个数据,最后这个合约是相对应字节码,然后由EVM解析,解析的结果通常就是存出去要么就是给别的帐号打钱,大概就是这样。

区块链现状&未来

区块链的现况大家体会一下,有点天下大乱的样子,区块链的新世界,这个是我们对未来的小展望,现在大概是5000万个用户在炒币,可能未来十年区块链会增长到10亿用户,像手机一样普及。如果这个事成真,每个人都会有一些数据的资产,变得像手机一样普及,这样的话我们要讨论。这就牵扯到扩容,扩容的话有几种方案我现在比较看好侧链和跨链的方案,如果我们要做产品那就考虑说这个到底怎么样做出好的产品给人用,所以说现在很多正在驱动的区块链产品很难用被各种喷;再一个是隐私的问题,区块链就是公开帐本,毫无隐私可言。

智能合约发现了很多的问题,智能合约在安全上或者是在可用性上面他学习成本是相当高的,因为它是不同的平台,所用的编程语言也与其他语言不同,一切都要从头开始学。现在我们可以看到一些新一代的智能合约平台可以出现了,像WEB 3.0是在浏览器里面做一个接近底层的版本。所以说我们可以看到像以太坊生态圈里面truebit在尝试然后像rholang这类项目,它也解决了合约并发的问题,像我们公司量子我现在做一个虚拟机,这个是模拟了CPU的架构,我们希望说通过这个架构可以支持一些比较底层的开发去做智能合约,然后我们会比较关注像操作系统和虚拟机的分层,现在你去分析EBM的原理,它是字节码,里面会看到一些属于操作系统的的这个指令,它其实是建在虚拟机里面,这个也是一个技术上的一些问题。刚刚也提到了一个问题,就是智能合约到底能不能更新,应该怎么更新,这也是现在行业里面讨论的问题,我们会采用一个像DGP这种方式,我们通过投票去让用户选择这个智能合约应该怎么更新。欢迎大家跟我交流,谢谢大家。