嘿,程序员!手把手教你写出智能合约"Hello, World"

299 阅读20分钟
原文链接: click.aliyun.com

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1

区块链大本营出品

参与 | Arvin、波波



由于Dapp(去中心化应用)涉及到智能合约概念、以太坊区块链机制等更为基础的知识点,仅从编程角度来讲解Dapp开发的入门,不免给人以空中楼阁、镜花水月的感受。所以,以太坊这份面向Dapp新手的开发教程,从去中心化应用最基础的知识点开始,一步一步介绍如何用以太坊客户端开发出一个最简答的智能合约,也就是Dapp的后端代码。


尽管这份Dapp开发入门教程成文已久,但它对于区块链开发新手的友好程度,是后来的很多开发教程都无法比拟的。为此,区块链大本营特地将其翻译成中文,以飨读者。Enjoy!



区块链基础


以太坊(Ethereum)创造的去中心化网络,被称为第三代网络(‘网络3’)。与第二代网络(‘web2’)所不同的是,在以太坊网络中没有中心式的网络服务器,因此“没有中间商拿差价”,没有人能够窃取你的数据或者将数据提供给美国国家安全局(National Security Agency,简写为NSA),当然也没有分布式拒绝服务(Distributed Denial of Service,简称为DDoS)。


一个去中心化应用程序(Dapp)由两部分组成:一个用HTML编写的前端和一个后端(可以将其理解为前端的‘数据库’)。


下面首先说个好消息:如果您对开发网站有一定了解,并且喜欢bootstrap或任何其他框架,您可以继续使用它们,因为去中心化应用程序的前端跟现有网络中的前端是一样的,同样可以被整个网络访问,并且可以访问CDN。实际上,对于所有的设计意图和使用目的,为去中心化应用程序编写HTML开发前端与开发网站完全相同,并且从web 2转换到web 3在很多情况下是非常简单的。


还有个更好的消息:通过使用回调函数,你可以获得反应式编程(这会让使用angular、meteor和derby框架的粉丝非常高兴),并且不用学习新的框架。


还有个甚至更好的消息:因为以太坊依靠密码原理运作,所以每个去中心化应用程序都知道每个用户的假身份。在去中心化的网络中,用户不需要‘创建账户’或‘登录’来访问你的去中心化应用程序,你可以认为这是web3的开放标识。


其实没有什么坏消息,除了可能的来自现有网络世界web2中的不良因素,比如与金钱或赌博网站进行不诚信交流、伪造随机数字信息以牟利。在以太坊(Ethereum)网络上,后端操作由网络上的所有节点验证,这意味着后端将始终执行其代码所说的操作。这就是为什么你可能会听到人们说以太坊(Ethereum)是‘去权威’的原因--用户不需要信任中央权威机构去‘做正确的事情’。



以太坊客户端


本教程将重点介绍Alethzero客户端的上手使用,以及如何用它创建简单的智能合约 ,也就是去中心化应用的后端程序。


重要提示:本教程使用的Alethzero客户端目前处于快速迭代期,最稳定的版本是您下载的最新版。如果您想体验最新版,可以随意切换到最新版的客户端,但请注意,本教程尚未经过测试可以无缝地用于您将使用的最新版本。


我们将使用Alethzero,这是为开发人员设计的以太坊(Ethereum)网络客户端的C ++实现。需要强调的是,我们将安装最新版,这是截止目前最稳定的,并包含所有最新功能的版本。


OSX和Windows平台上的可执行二进制文件可以从这里获得 (https://github.com/ethereum/cpp-ethereum/wiki) 。我们下面在Ubuntu平台上进行,其按照步骤请详看此链接(https://github.com/ethereum/cpp-ethereum/wiki/Installing%20Clients)。


如果一切顺利,在启动Alethzero之后,您应该看到如下图的内容,具体取决于您的平台和屏幕分辨率。


640?wx_fmt=png


如果您的屏幕分辨率不同,并且并非所有界面都可见,请手动关闭显示屏中的所有窗格,方法是单击它们的“x”符号,直到它们全部消失,调整屏幕大小以适应您的分辨率,然后通过右键单击来手动重新添加,添加按钮位于标题栏下方(在“刷新”按钮的右侧)。


我们首先介绍这个视图。在屏幕的中心是一个浏览器窗口,确切地说是一个Webkit视图。您可以像浏览任何其他浏览器一样浏览现有的网页,例如百度。


其余面板包含我们稍后将使用的调试和技术信息。尽管对开发人员有用,但这不是一个非常友好的用户界面,因此以太坊计划开发最新版的浏览器(暂定名为“Mist”),而且计划构建在Go Ethereum实现之上,且具有完全不同的外观和体验。事实上,在以太坊更新其浏览器后,其界面将像下面这样:


640?wx_fmt=png


以上是我们Amsterdam团队正在开发的以太坊浏览器“Mist”的一种可能的外观概念,作为一个访问去中心化应用程序的商店。当体验不佳时,请随时重新调整您的屏幕。您还可以将面板拖放到彼此的顶部以堆叠它们。



选择什么


以太坊是一个通用的编程平台。您可以在上面构建游戏,金融应用程序,赌博应用程序,保险公司,社交网络,以及任何事情!但是,首先记住用户的需求总是好事,你必须知道他们在集中式网络应用中缺少什么?


在现在这个教程中,我们将编写一个简单的智能合约(智能合约的功能有点像银行,但是有一个全球可以审计的透明总帐)应用来发行通证。我们将发行10000个通证(token,用于代表一个价值实体),如果我们只是自己持有这些通证肯定就不会有那么多乐趣,所以我们还会创建一个方法以允许我们将它们发送给我们的朋友。


其实,我们就是在发行自己的货币,虽然是以一种非常基本的方式,但本质上是这样的。在web2世界中,我们可以用PHP和MySQL轻松地构建一个这样的应用程序,但您的用户必须相信您做好以下的所有事情:拥有诚实的会计师,分类帐保持一致,政府没有克扣资金,黑客不会闯入服务器,员工在任何时候都是可靠的且没有设置后门,程序员不会犯一个错误,还有许多许多......现在,你看出web3和web2的区别了吧。



智能合约


我们的后端(即以太坊网络中的‘智能合约’)将使用一种名为Solidity的编程语言。当然,在构建以太坊网络后端时,您可以使用其他几种语言,包括LLL(类似于Lisp)和Serpent(类似于Python)。我们将使用 Solidity,因为它是ETHDEV团队官方支持的语言。


正如我们上面提到的,我们正在建立一个小银行,所以我们需要做两件事:


  • 实例化至少一个有帐号余额的通证,以便支撑智能合约第一次创建时的启用。

  • 构建一个类似于contract.send(account,amount)的“发送”函数,来让通证流动起来。


让我们继续深入,请随意打开您最喜爱的文本编辑器来编辑您的第一份智能合约代码:


1contract metaCoin {    
2    mapping (address => uint) balances;
3    function  metaCoin() {
4        balances[msg.sender] = 10000;
5    }
6    function  sendCoin(address receiver, uint amount) returns( bool sufficient) {
7        if (balances[msg.sender] < amount)  return false;
8        balances[msg.sender] -= amount;
9        balances[receiver] += amount;
10        return true;
11    }
12}


不要惊慌,它比看起来简单得多。智能合约由方法组成。第一种称为metaCoin的方法是一种特殊的构造函数方法,用于定义智能合约数据存储的初始状态。构造函数的名称始终与智能合约名称相同。这个初始化代码在创建智能合约时只运行一次,而不会再次运行。智能合约的第二部分是智能合约代码本身,永远在以太坊网络上存在的,永远不变的内容,它将在数百万节点上运行以确保它每次都能返回预期的结果。在我们的例子中,这是一个简单的函数,用于检查发件人是否有足够多的余额,如果是,则将通证从一个帐户转移到另一个帐户。


我们来看看更详细的内容:


1mapping (address => uint) balances;


这一行创建一个存储映射,您的代码可以严格地将信息写入智能合约存储,在这里我们已经定义了一个类型为address和uint的键值对的映射,这个整体有个名字,余额(balances)。这就是我们将存储用户通证余额的地方(当然这些代码需要执行在以太坊网络上才能生效)。


请注意,我们已经指定了两种数据类型,地址和uint。与Serpent不同,Solidity是静态类型的,将在编译时执行类型检查。在编译之前指定类型还允许我们减小事务传递的数据数组的大小,并允许编译器创建更多优化的EVM代码。


1     function  metaCoin() {
2        balances[msg.sender] = 10000;
3    }


这是我们智能合约的初始化:它只会运行一次(因为它在构造函数方法'metaCoin'中),它将做以下操作。首先,它使用msg.sender来查找交易发件人的公开地址;其次,它使用我们的映射访问我们的智能合约存储的余额(balances)。智能合约将数据存储为长度为32个字节的键值对。


msg.sender 是一个表示您公钥的160位数字,这是一个以太坊网络上的无法伪造的独特标识符,当然这得益于以太坊实现的加密法则。还有其他的事情,以太坊保证智能合约收到真实可靠的交易 -- 我们将在其他教程中探讨这些。


现在我们已经分配了一个初始余额,接下来让我们来看看'sendCoin'函数,这个函数在我们每次调用智能合约时执行。这是用户可以调用的唯一可执行函数,而我们的初始化函数不能再次被调用。


1function  sendCoin(address receiver, uint amount) returns( bool sufficient) {
2        if (balances[msg.sender] < amount)  return false;
3        balances[msg.sender] -= amount;
4        balances[receiver] += amount;
5        return true;
6    }


这个函数有两个传递给它的参数:receiver是代表接收者的另一个160位公共地址,amount 是你希望发送给接收者的通证数量。


在第一行中,我们将检查msg.sender的当前余额是否小于我们希望发送的通证的数量。如果是,我们将返回空,并且不执行接下来的两行代码。这是合理的,因为调用智能合约的账户是发送令牌的账户,并且它必须有足够多的通证来完成交易。


因此,为了将通证从一个存储地址转移到另一个存储地址,传递给我们函数'transfer'的三个参数必须是:msg.sender,receiver和amount。


如果余额足够,条件评估将通过,接下来的两行将减去发件人余额发送的金额: balances[msg.sender] -= amount; ,然后添加到接收通证的帐户余额: balances[receiver] += amount; 。


所以现在我们有了进行通证流动的功能,将通证从一个账户的控制下转移到另一个账户。



不要用光瓦斯!


在我们走得更远之前,有一个重要的概念需要了解,它将帮助您进一步理解以太坊智能合约,它就是瓦斯(Gas)。


让我们来看看下面的内容:为了给去中心化网络提供动力,以太坊不可能依赖任何集中的权限(因为有权限意味着可以操纵数据库)。相反,参与网络的每个节点都拥一个数据库副本,并独立进行审计。


网络节点在数据库中处理正在执行的代码,并通过表决就数据库的正确状态达成一致。多数人总是赢得选票,并且节点被激励来做这种验证工作。投票通常每隔一段时间进行,平均每隔12.7秒进行一次投票。


我们之前编写的智能合约将存储在这个数据库中。智能合约在用户或其他智能合约调用时将被触发执行。


如果您认为这种必要的方法在处理速度方面存在限制,那么您是对的。以太坊网络的总处理能力,无论其形成的节点数量如何,都等于1999年的一款智能手机。这意味着您不会希望在以太坊网络上存储上兆字节的数据,或者渲染3d图形。当然有一些解决方法,包括我们即将推出的存储解决方案Swarm和我们的安全消息协议Whisper,这两种配套技术都适用于以太坊。


这也意味着,因为计算能力是有限的,所以必须仔细衡量,以便没有一个单独的行为者能够犯下邪恶的行为,例如在世界以太坊节点上运行无限循环。这个度量单位被称为瓦斯(“Gas”)。


当你尝试签订一份智能合约时,瓦斯会起作用。您可以调用智能合约的一个函数,然后执行该函数中的代码。它可以验证托管服务,可以为‘分布式社交应用程序’中的好友点赞,可以将一定数量的智能合约规定的通证传输给另一个用户,等等。


为了执行这个功能,智能合约将需要瓦斯,就像你的汽车需要燃料一样。因此,作为函数调用的一部分,您需要指定您想要发送给智能合约的瓦斯数量,以及您愿意为该瓦斯支付多少费用(价格按以太币计算,这是以太坊的“燃料”和计费单位)。


智能合约可以支持的不同操作的价格是不同的。例如,一个执行循环耗费一个单位瓦斯。其他的,比如写入存储,成本要高得多(因为存储是非常稀缺的资源)。如果您向智能合约中发送过多的瓦斯,但并未全部使用,以太坊会退还给您。如果发送得太少,智能合约将停止并回滚(就像您的汽车在燃料不足时无法前进一样)。


瓦斯的定价取决于社区的全球共识。因此,瓦斯报价最高的操作将首先在网络上执行,稍后再运行其他低价操作。



上传智能合约


现在您已经了解了以太坊的基本原理,并且您已经写好了第一份智能合约,现在是时候将它部署到以太坊网络上。


如果您尚未这样做,请打开您的Alethzero客户端,并熟悉该界面。确保你手边已经打开了一个记事本,并编辑好前面讲述的智能合约代码。为了本教程的目的,您需要先断开到Testnet的连接,然后从调试菜单中选择“使用私有链”并创建您自己的私有链。这样,当你开发你的去中心化应用时,你不依赖于在线测试。点击菜单栏中标记为“新交易”(“New Transaction”)的按钮,弹出新的交易对话框。这是您输入智能合约代码和发送交易的地方,它包括发送交易的地址,瓦斯和瓦斯价格(gasPrice)的选项以及用于输入智能合约代码或交易数据的窗格。现在关闭它。正如前面提到的,保存这份智能合约或者运行它需要瓦斯或者说以太币,但是目前我们只有0个单位。为了获得一点以太币,您需要参与投票过程,以保证分散数据库的完整性,这个过程就是俗称的“挖矿”。


在这种情况下,您没有连接到网络,因为您运行的是私有链,所以您只能挖掘新块。点击工具栏中的‘矿池’(Mine)-- 然后进入调试并选择‘强制挖掘’(force mining)。您应该开始看到各种标签信息。采矿标签页有一个挖矿可视化界面 -- 当你成功挖到一个块时,会出现红色尖峰,这样你的余额就会增加。在这个过程中有一个有趣的标签页是区块链(Blockchain)-- 这里记录着每一次投票并用一个数字进行标记。先卖个关子,待会我们会继续讲述相关内容。


一旦你是15000个以太币的持有者时,你可以骄傲地停止采矿。再次点击‘矿池’(Mine)来停止挖矿。现在让我们部署我们的智能合约。


重新打开新交易弹窗,复制我们之前写的智能合约代码并粘贴在‘Data’文本框中。现在界面如下图所示:


640?wx_fmt=png


由于我们正在尝试创建智能合约并且不会将以太币发送给其他帐户,因此您可以安全地将所有字段保留为默认值。我们将以10 Szabo的价格标定每单位瓦斯来发送智能合约。Szabo是另一个价值单位,相当于0.000001以太币。由于我们的合同创建代码非常简单,因此我们将得到退还的大部分费用,因为智能合约的保存不会消耗所有的瓦斯。


如果您有任何编译错误,很可能是因为您没有正确复制合约--如果调试器发现错误,它会为您提供相关的行和信息。如果您已正确粘贴智能合约代码,则会看到合约代码下面的窗格中显示的两条消息。第一个标题为“Solidity”包含了看起来像JavaScript的代码段的内容,其中包括智能合约中所有可执行函数的列表,最重要的是我们的函数‘sendCoin’(它充当ABI,应用程序字节接口,即Application Byte Interface)。将这些信息复制到记事本中,稍后您还需要它。第二条消息是类似于“PUSH2 0x27 0x10 CALLER ...”的类似汇编的指令。这是您的智能合约的EVM代码,以编译后的形式呈现。


现在您的智能合约已经编译好了,您只需按执行(‘Execute’)按钮,即可在全球的去中心化数据库中部署。由于您还没有开采“矿石”,因此您的智能合约创建会落入“待处理”(Pending)状态。“待处理”窗格视图如下:


640?wx_fmt=png


在您按下执行(‘Execute’)按钮后,如果您查看“待处理”(Pending)窗格中特定事务的‘create’字段,会有类似‘1f530b6b ...’的内容(当然,因为每个智能合约会创建一个唯一的ID,您的结果肯定和这个不一样)。


现在您有一个“待处理”(Pending)智能合约,您需要通过挖掘一个块来将这个事务提交到区块链上(在一个实时网络上,任何其他挖掘的人都将接收到这个事务,并试图将其包含在挖掘到的块中)。点击‘矿池’(Mine)按钮进行挖矿,直到事务状态从“待处理”(Pending)转为“智能合约”(Contract)状态。点击‘矿池’(Mine)按钮再次关闭挖矿,并在智能合约窗格中单击您的新合约。您将看到如下所示的内容:


640?wx_fmt=png


您可以从智能合约“数据存储”(data storage)中的一个条目中看到显示了您的公共地址的SHA3键值和数字“10,000”。一切看起来不错,现在我们可以认为,至少我们的构造函数已经正确运行。现在我们想测试一下我们是否可以将通证发送给另一个用户的公共地址,为了运行我们的sendCoin功能,我们需要发送一个事务到合约地址,指定一个目的地址(‘to’指定的框),和一个数量(‘Value’),最重要的是交易数据数组中的一个函数ID。找到事务处理窗格并将合约地址粘贴到“地址”框中,结果如下:


640?wx_fmt=png


现在在我们之前输入代码的区域中,我们首先将函数ID和收件人地址和值分别放在不同的行上。还记得前面的操作步骤中保存的‘sendCoin’函数的ID吗?我们使用0xec6d9353ca85eb80076817fa989f8825e136d55d(这是我的以太坊网络地址)和任何低于10000的值。如下所示:


1$0x90b98a11
20xec6d9353ca85eb80076817fa989f8825e136d55d
3500


每次在智能合约中调用函数时,其格式都是这样的:4字节函数ID,后面跟着函数参数(整个数据会自动填充为32个字节)。在后面的教程中你会看到更多的例子。


现在我们执行一个新的操作来重温一下。首先点击执行,你应该再次看到一个待处理的事务,然后点击矿池,直到你生成一个新块,然后停止挖掘。你的智能合约现在应该看起来像这样:


640?wx_fmt=png



写在最后


圆满完成!现在你的第一个智能合约就被创建出来了。如果你对它的效果感到满意,就可以将智能合约上传到测试链实际加以测试。



原文发布时间为:2018年03月09日
本文作者:区块链大本营
本文来源:CSDN区块链大本营,如需转载请联系原作者。