区块链:教程以太坊智能合约编程之菜鸟教程

区块链:教程以太坊智能合约编程之菜鸟教程

2023年6月30日发(作者:)

区块链:教程以太坊智能合约编程之菜鸟教程这篇介绍以太坊合约的⽂章写得很好,在查找了这么多资料,进⾏对⽐之后,感觉阅读这⼀篇就可以⼤体理解以太坊编程的原理,如果对个别的知识点还有点含糊,可以相应地去查⼀查,就是以这篇为主⼲,别的资料为辅。稍微整理了⼀下格式,以及修改了⼀些半⾓符号。译注:⾸发于ConsenSys开发者博客,原作者为Eva以及ConsenSys的开发团队。如果您想要获取更多及时信息,可以访问⾸页点击左下⾓Newsletter订阅邮件。本⽂的翻译获得了ConsenSys创始⼈Lubin先⽣的授权。有些⼈说以太坊太难对付,于是我们(译注:指, 下同)写了这篇⽂章来帮助⼤家学习如何利⽤以太坊编写智能合约和应⽤。这⾥所⽤到的⼯具,钱包,应⽤程序以及整个⽣态系统仍处于开发状态,它们将来会更好⽤!概述,讨论了关键概念,⼏⼤以太坊客户端以及写智能合约⽤到的编程语⾔。讨论了总体的⼯作流程,以及⽬前流⾏的⼀些DApp框架和⼯具。主要关于编程,我们将学习如何使⽤Truffle来为智能合约编写测试和构建DApp。第⼀部分 概述如果你觉得⽩⽪书中的章节太晦涩,也可以直接动⼿来熟悉以太坊。在以太坊上做开发并不要求你理解所有那些“密码经济计算机科学”(cryptoeconomic computer science),⽽⽩⽪书的⼤部分是关于以太坊想对于⽐特币架构上的改进。新⼿教程提供了官⽅的新⼿⼊门教程,以及⼀个代币合约和众筹合约的教程。合约语⾔Solidity也有。学习智能合约的另⼀份不错的资料(也是我的⼊门资料)是,不过现在可能有些过时了。这篇⽂章的⽬的是成为上述资料的补充,同时介绍⼀些基本的开发者⼯具,使⼊门以太坊,智能合约以及构建DApps(decentralized apps, 分布式应⽤)更加容易。我会试图按照我⾃⼰(依然是新⼿)的理解来解释⼯作流程中的每⼀步是在做什么,我也得到了ConsenSys酷酷的开发者们的许多帮助。基本概念了解这些名词是⼀个不错的开始:公钥加密系统。 Alice有⼀把公钥和⼀把私钥。她可以⽤她的私钥创建数字签名,⽽Bob可以⽤她的公钥来验证这个签名确实是⽤Alice的私钥创建的,也就是说,确实是Alice的签名。当你创建⼀个以太坊或者⽐特币钱包的时候,那长长的5f地址实质上是个公钥,对应的私钥保存某处。类似于Coinbase的在线钱包可以帮你保管私钥,你也可以⾃⼰保管。如果你弄丢了存有资⾦的钱包的私钥,你就等于永远失去了那笔资⾦,因此你最好对私钥做好备份。过来⼈表⽰:通过踩坑学习到这⼀点是⾮常痛苦的…点对点⽹络。 就像BitTorrent, 以太坊分布式⽹络中的所有节点都地位平等,没有中⼼服务器。(未来会有半中⼼化的混合型服务出现为⽤户和开发者提供⽅便,这我们后⾯会讲到。)区块链。 区块链就像是⼀个全球唯⼀的帐簿,或者说是数据库,记录了⽹络中所有交易历史。以太坊虚拟机(EVM)。 它让你能在以太坊上写出更强⼤的程序(⽐特币上也可以写脚本程序)。它有时也⽤来指以太坊区块链,负责执⾏智能合约以及⼀切。节点。 你可以运⾏节点,通过它读写以太坊区块链,也即使⽤以太坊虚拟机。完全节点需要下载整个区块链。轻节点仍在开发中。矿⼯。 挖矿,也就是处理区块链上的区块的节点。这个⽹页可以看到当前活跃的⼀部分以太坊矿⼯:。⼯作量证明。 矿⼯们总是在竞争解决⼀些数学问题。第⼀个解出答案的(算出下⼀个区块)将获得以太币作为奖励。然后所有节点都更新⾃⼰的区块链。所有想要算出下⼀个区块的矿⼯都有与其他节点保持同步,并且维护同⼀个区块链的动⼒,因此整个⽹络总是能达成共识。(注意:以太坊正计划转向没有矿⼯的权益证明系统(POS),不过那不在本⽂讨论范围之内。)以太币。 缩写ETH。⼀种你可以购买和使⽤的真正的数字货币。这⾥是可以交易以太币的其中⼀家交易所的。在写这篇⽂章的时候,1个以太币价值65美分。Gas。(汽油) 在以太坊上执⾏程序以及保存数据都要消耗⼀定量的以太币,Gas是以太币转换⽽成。这个机制⽤来保证效率。DApp。 以太坊社区把基于智能合约的应⽤称为去中⼼化的应⽤程序(Decentralized App)。DApp的⽬标是(或者应该是)让你的智能合约有⼀个友好的界⾯,外加⼀些额外的东西,例如IPFS(可以存储和读取数据的去中⼼化⽹络,不是出⾃以太坊团队但有类似的精神)。DApp可以跑在⼀台能与以太坊节点交互的中⼼化服务器上,也可以跑在任意⼀个以太坊平等节点上。(花⼀分钟思考⼀下:与⼀般的⽹站不同,DApp不能跑在普通的服务器上。他们需要提交交易到区块链并且从区块链⽽不是中⼼化数据库读取重要数据。相对于典型的⽤户登录系统,⽤户有可能被表⽰成⼀个钱包地址⽽其它⽤户数据保存在本地。许多事情都会与⽬前的web应⽤有不同架构。)如果想看看从另⼀个新⼿视⾓怎么理解这些概念,请读。以太坊客户端,智能合约语⾔编写和部署智能合约并不要求你运⾏⼀个以太坊节点。下⾯有列出。但如果是为了学习的话,还是应该运⾏⼀个以太坊节点,以便理解其中的基本组件,何况运⾏节点也不难。运⾏以太坊节点可⽤的客户端以太坊有许多不同语⾔的客户端实现(即多种与以太坊⽹络交互的⽅法),包括C++, Go, Python, Java, Haskell等等。为什么需要这么多实现?不同的实现能满⾜不同的需求(例如Haskell实现的⽬标是可以被数学验证),能使以太坊更加安全,能丰富整个⽣态系统。在写作本⽂时,我使⽤的是Go语⾔实现的客户端geth (),其他时候还会使⽤⼀个叫testrpc的⼯具, 它使⽤了Python客户端。后⾯的例⼦会⽤到这些⼯具。注: 我曾经使⽤过C++的客户端,现在仍然在⽤其中的ethminer组件和geth配合挖矿,因此这些不同的组件是可以⼀起⼯作的。

关于挖矿:挖矿很有趣,有点像精⼼照料你的室内盆栽,同时⼜是⼀种了解整个系统的⽅法。虽然以太币现在的价格可能连电费都补不齐,但以后谁知道呢。⼈们正在创造许多酷酷的DApp, 可能会让以太坊越来越流⾏。交互式控制台。 客户端运⾏起来后,你就可以同步区块链,建⽴钱包,收发以太币了。使⽤geth的⼀种⽅式是通过(JavaScript console, 类似你在chrome浏览器⾥⾯按F12出来的那个,只不过是跑在终端⾥)。此外还可以使⽤类似cURL的命令通过来与客户端交互。本⽂的⽬标是带⼤家过⼀边DApp开发的流程,因此这块就不多说了。但是我们应该记住这些命令⾏⼯具是调试,配置节点,以及使⽤钱包的利器。在测试⽹络运⾏节点。 如果你在正式⽹络运⾏geth客户端,下载整个区块链与⽹络同步会需要相当时间。(你可以通过⽐较节点⽇志中打印的最后⼀个块号和上列出的最新块来确定是否已经同步。) 另⼀个问题是在正式⽹络上跑智能合约需要实实在在的以太币。在测试⽹络上运⾏节点的话就没有这个问题。此时也不需要同步整个区块链,创建⼀个⾃⼰的私有链就勾了,对于开发来说更省时间。testrpc。 ⽤geth可以创建⼀个测试⽹络,另⼀种更快的创建测试⽹络的⽅法是使⽤testrpc。Testrpc可以在启动时帮你创建⼀堆存有资⾦的测试账户。它的运⾏速度也更快因此更适合开发和测试。你可以从testrpc起步,然后随着合约慢慢成型,转移到geth创建的测试⽹络上 - 启动⽅法很简单,只需要指定⼀个networkid:geth --networkid "12345"。这⾥是,下⽂我们还会再讲到它。接下来我们来谈谈可⽤的编程语⾔,之后就可以开始真正的编程了。写智能合约⽤的编程语⾔⽤Solidity就好。 要写智能合约有好⼏种语⾔可选:有点类似Javascript的Solidity, ⽂件扩展名是.sol和Python接近的Serpent, ⽂件名以.se结尾。还有类似Lisp的LLL。Serpent曾经流⾏过⼀段时间,但现在最流⾏⽽且最稳定的要算是Solidity了,因此⽤Solidity就好。听说你喜欢Python? ⽤Solidity。solc编译器。 ⽤Solidity写好智能合约之后,需要⽤solc来编译。它是⼀个来⾃C++客户端实现的组件(⼜⼀次,不同的实现产⽣互补),是安装⽅法。如果你不想安装solc也可以直接使⽤基于浏览器的编译器,例如或者。后⽂有关编程的部分会假设你安装了solc。注意:以太坊正处于积极的开发中,有时候新的版本之间会有不同步。确认你使⽤的是最新的dev版本,或者稳定版本。如果遇到问题可以去以太坊项⽬对应的Gitter聊天室或者上问问其他⼈在⽤什么版本。 API。 当Solidity合约编译好并且发送到⽹络上之后,你可以使⽤以太坊的来调⽤它,构建能与之交互的web应⽤。以上就是在以太坊上编写智能合约和构建与之交互的DApp所需的基本⼯具。第⼆部分 DApp框架,⼯具以及⼯作流程DApp开发框架虽然有上⽂提到的⼯具就可以进⾏开发了,但是使⽤社区⼤神们创造的框架会让开发更容易。Truffle and Embark。 是把我领进了门。在Truffle出现之前的那个夏天,我⽬睹了⼀帮有天分的学⽣是如何不眠不休的参加⼀个hackathon(编程马拉松)活动的,虽然,但我还是吓到了。然后Truffle出现了,帮你处理掉⼤量⽆关紧要的⼩事情,让你可以迅速进⼊写代码-编译-部署-测试-打包DApp这个流程。另外⼀个相似的DApp构建与测试框架是。我只⽤过Truffle, 但是两个阵营都拥有不少DApp⼤神。Meteor。 许多DApp开发者使⽤的另⼀套开发栈由和组成,Meteor是⼀套通⽤webapp开发框架(项⽬提供了⼀个很棒的⼊门实例,⽽正在构建⼤量Meteor与和DApp集成的模板)。我下载并运⾏过⼀些不错的DApp是以这种⽅式构造的。在11⽉9⽇⾄13⽇的上将有⼀些有趣的讨论,是关于使⽤这些⼯具构建DApp以及相关最佳实践的(会议将会在上直播)。APIs。 打算提供⼀套RESTful API给DApp使⽤以免去开发者运⾏本地节点的⿇烦,这个中⼼化服务是基于以太坊Haskell实现的。这与DApp的去中⼼化模型背道⽽驰,但是在本地⽆法运⾏以太坊节点的场合⾮常有⽤,⽐如在你希望只有浏览器或者使⽤移动设备的⽤户也能使⽤你的DApp的时候。BlockApps提供了⼀个命令⾏⼯具,注册⼀个开发者帐号之后就可以使⽤。许多⼈担⼼需要运⾏以太坊节点才能使⽤DApp的话会把⽤户吓跑,其实包括BlockApps在内的许多⼯具都能解决这个问题。允许你在浏览器⾥⾯使⽤以太坊的功能⽽⽆需节点,以太坊官⽅提供的AlethZero或者AlethOne是正在开发中有易⽤界⾯的客户端,ConsenSys正在打造⼀个轻钱包,这些⼯具都会让DApp的使⽤变得更容易。和⽔平分⽚(sharding)也在计划和开发之中。这是⼀个能进化出混合架构的P2P⽣态系统。智能合约集成开发环境 (IDE)IDE。 以太坊官⽅出品了⽤来编写智能合约的,我还没⽤过但会尽快⼀试。基于浏览器的IDE。 和都可以让你快速开始在浏览器中编写智能合约。你甚⾄可以让这些⼯具使⽤你的本地节点,只要让本地节点开⼀个端⼝(注意安全!这些⼯具站点必须可信,⽽且千万不要把你的全部⾝家放在这样⼀个本地节点⾥⾯!上有如何使⽤geth做到这⼀点的指引)。在你的智能合约调试通过之后,可以⽤开发框架来给它添加⽤户界⾯和打包成DApp,这正是Truffle的⼯作,后⾯的编程章节会有详细讲解。正在开发另⼀个强⼤的企业级浏览器IDE。他们的IDE将⽀持沙盒测试⽹络,⾃动⽣成⽤于测试的⽤户界⾯(取代后⽂将展⽰的⼿动编写测试),以及⼀个测试交易浏览器。当你的合约准备正式上线之前,使⽤他们的测试⽹络会是确保你的智能合约在⼀个接近真实的环境⼯作正常的好⽅法。他们也为正式⽹络提供了⼀个交易浏览器,上⾯可以看到每⼀笔交易的细节。在本⽂写作时的IDE还只能通过邀请注册,预计很快会正式发布。合约和Dapp⽰例。 在Github上搜索DApp仓库和.sol⽂件可以看到进⾏中的有趣东西。这⾥有⼀个DApp⼤列表:,不过其中⼀些项⽬已经过时。上有⼀些Solidity和Serpent写的合约⽰例,但是不清楚这些例⼦有没有经过测试或者正确性验证。11⽉12⽇的将会有⼀整天的DApp主题演讲。部署智能合约的流程流程如下:1. 启动⼀个以太坊节点 (例如geth或者testrpc)。2. 使⽤solc*_编译_*智能合约。 => 获得⼆进制代码。3. 将编译好的合约部署到⽹络。(这⼀步会消耗以太币,还需要使⽤你的节点的默认地址或者指定地址来给合约签名。) => 获得合约的区块链地址和ABI(合约接⼝的JSON表⽰,包括变量,事件和可以调⽤的⽅法)。(译注:作者在这⾥把ABI与合约接⼝弄混了。ABI是合约接⼝的⼆进制表⽰。)4. ⽤提供的JavaScript API来调⽤合约。(根据调⽤的类型有可能会消耗以太币。)下图详细描绘了这个流程:你的DApp可以给⽤户提供⼀个界⾯先部署所需合约再使⽤之(如图1到4步),也可以假设合约已经部署了(常见⽅法),直接从使⽤合约(如图第6步)的界⾯开始。第三部分 编程在Truffle中进⾏测试⽤来做智能合约的测试驱动开发(TDD)⾮常棒,我强烈推荐你在学习中使⽤它。它也是学习使⽤JavaScript Promise的⼀个好途径,例如deferred和异步调⽤。Promise机制有点像是说“做这件事,如果结果是这样,做甲,如果结果是那样,做⼄… 与此同时不要在那⼉⼲等着结果返回,⾏不?”。Truffle使⽤了包装的⼀个JS Promise框架(因此它为为你安装)。(译注:Promise是流⾏于JavaScript社区中的⼀种异步调⽤模式。它很好的封装了异步调⽤,使其能够灵活组合,⽽不会陷⼊callback hell.)Transaction times。 Promise对于DApp⾮常有⽤,因为交易写⼊以太坊区块链需要⼤约12-15秒的时间。即使在测试⽹络上看起来没有那么慢,在正式⽹络上却可能会要更长的时间(例如你的交易可能⽤光了Gas,或者被写⼊了⼀个孤⼉块)。下⾯让我们给⼀个简单的智能合约写测试⽤例吧。使⽤Truffle⾸先确保你 1.安装好了以及 2.。(testrpc需要和。如果你是Python新⼿,你可能需要⽤来安装,这可以将Python程序库安装在⼀个独⽴的环境中。)接下来安装 3.(你可以使⽤来安装:npm install -g truffle,

-g开关可能会需要sudo)。安装好之后,在命令⾏中输⼊truffle list来验证安装成功。然后创建⼀个新的项⽬⽬录(我把它命名为’conference’),进⼊这个⽬录,运⾏truffle init。该命令会建⽴如下的⽬录结构:现在让我们在另⼀个终端⾥通过执⾏testrpc来启动⼀个节点(你也可以⽤geth):回到之前的终端中,输⼊truffle deploy。这条命令会部署之前truffle init产⽣的模板合约到⽹络上。任何你可能遇到的错误信息都会在testrpc的终端或者执⾏truffle的终端中输出。在开发过程中你随时可以使⽤truffle compile命令来确认你的合约可以正常编译(或者使⽤solc ),truffle deploy来编译和部署合约,最后是truffle test来运⾏智能合约的测试⽤例。第⼀个合约下⾯是⼀个针对会议的智能合约,通过它参会者可以买票,组织者可以设置参会⼈数上限,以及退款策略。本⽂涉及的所有代码都可以在这个找到。contract Conference { address public organizer; mapping (address => uint) public registrantsPaid; uint public numRegistrants; uint public quota; event Deposit(address _from, uint _amount); // so you can log these events event Refund(address _to, uint _amount); function Conference() { // Constructor organizer = ; quota = 500; numRegistrants = 0; } function buyTicket() public returns (bool success) { if (numRegistrants >= quota) { return false; } registrantsPaid[] = ; numRegistrants++; Deposit(, ); return true; } function changeQuota(uint newquota) public { if ( != organizer) { return; } quota = newquota; } function refundTicket(address recipient, uint amount) public { if ( != organizer) { return; } if (registrantsPaid[recipient] == amount) {

address myAddress = this; if (e >= amount) {

(amount); registrantsPaid[recipient] = 0; numRegistrants--; Refund(recipient, amount); } } } function destroy() { // so funds not locked in contract forever if ( == organizer) {

suicide(organizer); // send funds to organizer } }}接下来让我们部署这个合约。(注意:本⽂写作时我使⽤的是Mac OS X 10.10.5, solc 0.1.3+ (通过brew安装),Truffle v0.2.3, testrpc v0.1.18 (使⽤venv))部署合约(译注:图中步骤翻译如下:)使⽤truffle部署智能合约的步骤:

1.

truffle init (在新⽬录中) => 创建truffle项⽬⽬录结构

2. 编写合约代码,保存到contracts/⽂件。

3. 把合约名字加到config/的’contracts’部分。

4. 启动以太坊节点(例如在另⼀个终端⾥⾯运⾏testrpc)。

5.

truffle deploy(在truffle项⽬⽬录中)添加⼀个智能合约。 在truffle init执⾏后或是⼀个现有的项⽬⽬录中,复制粘帖上⾯的会议合约到contracts/⽂件中。然后打开config/⽂件,把’Conference’加⼊’deploy’数组中。启动testrpc。 在另⼀个终端中启动testrpc。编译或部署。 执⾏truffle compile看⼀下合约是否能成功编译,或者直接truffle deploy⼀步完成编译和部署。这条命令会把部署好的合约的地址和ABI(应⽤接⼝)加⼊到配置⽂件中,这样之后的truffle test和truffle build步骤可以使⽤这些信息。出错了? 编译是否成功了?记住,错误信息即可能出现在testrpc终端也可能出现在truffle终端。重启节点后记得重新部署! 如果你停⽌了testrpc节点,下⼀次使⽤任何合约之前切记使⽤truffle deploy重新部署。testrpc在每⼀次重启之后都会回到完全空⽩的状态。合约代码解读让我们从智能合约头部的变量声明开始:address public organizer;mapping (address => uint) public registrantsPaid;uint public numRegistrants;uint public quota;address。 地址类型。第⼀个变量是会议组织者的钱包地址。这个地址会在合约的构造函数function Conference()中被赋值。很多时候也称呼这种地址为’owner’(所有⼈)。uint。 ⽆符号整型。区块链上的存储空间很紧张,保持数据尽可能的⼩。public。 这个关键字表明变量可以被合约之外的对象使⽤。private修饰符则表⽰变量只能被本合约(或者衍⽣合约)内的对象使⽤。如果你想要在测试中通过使⽤合约中的某个变量,记得把它声明为public。Mapping或数组。(译注:Mapping类似Hash, Directory等数据类型,不做翻译。)在Solidity加⼊数组类型之前,⼤家都使⽤类似mapping(address => uint)的Mapping类型。这个声明也可以写作address registrantsPaid[],不过Mapping的存储占⽤更⼩(smaller footprint)。这个Mapping变量会⽤来保存参加者(⽤他们的钱包地址表⽰)的付款数量以便在退款时使⽤。关于地址。 你的客户端(⽐如testrpc或者geth)可以⽣成⼀个或多个账户/地址。testrpc启动时会显⽰10个可⽤地址:第⼀个地址,

accounts[0],是发起调⽤的默认地址,如果没有特别指定的话。组织者地址 vs 合约地址。 部署好的合约会在区块链上拥有⾃⼰的地址(与组织者拥有的是不同的地址)。在Solidity合约中可以使⽤this来访问这个合约地址,正如refundTicket函数所展⽰的:address myAddress = this;Suicide, Solidity的好东西。(译注:suicide意为’⾃杀’, 为Solidity提供的关键字,不做翻译。)转给合约的资⾦会保存于合约(地址)中。最终这些资⾦通过destroy函数被释放给了构造函数中设置的组织者地址。这是通过suicide(orgnizer);这⾏代码实现的。没有这个,资⾦可能被永远锁定在合约之中(reddit上有些⼈就遇到过),因此如果你的合约会接受资⾦⼀定要记得在合约中使⽤这个⽅法!如果想要模拟另⼀个⽤户或者对⼿⽅(例如你是卖家想要模拟⼀个买家),你可以使⽤可⽤地址数组中另外的地址。假设你要以另⼀个⽤户,accounts[1], 的⾝份来买票,可以通过from参数设置:ket({ from: accounts[1], value: some_ticket_price_integer });函数调⽤可以是交易。 改变合约状态(修改变量值,添加记录,等等)的函数调⽤本⾝也是转账交易,隐式的包含了发送⼈和交易价值。因此的函数调⽤可以通过指定{ from: __, value: __ }参数来发送以太币。在Solidity合约中,你可以通过和来获取这些信息:function buyTicket() public { ... registrantsPaid[] = ; ...}事件(Event)。 可选的功能。合约中的Deposit(充值)和Send(发送)事件是会被记录在以太坊虚拟机⽇志中的数据。它们实际上没有任何作⽤,但是⽤事件(Event)把交易记录进⽇志是好的做法。好了,现在让我们给这个智能合约写⼀个测试,来确保它能⼯作。写测试把项⽬⽬录test/中的⽂件重命名为,⽂件中所有的’Example’替换为’Conference’。contract('Conference', function(accounts) { it("should assert true", function(done) { var conference = (ed_address); (true); done(); // stops tests at this point });});在项⽬根⽬录下运⾏truffle test,你应该看到测试通过。在上⾯的测试中truffle通过ed_address获得合约部署在区块链上的地址。让我们写⼀个测试来初始化⼀个新的Conference,然后检查变量都正确赋值了。将中的测试代码替换为:contract('Conference', function(accounts) { it("Initial conference settings should match", function(done) { var conference = (ed_address);

// same as previous example up to here ({ from: accounts[0] }) .then(function(conference) { ().then( function(quota) { (quota, 500, "Quota doesn't match!");

}).then( function() { return (); }).then( function(num) { (num, 0, "Registrants should be zero!"); return (); }).then( function(organizer) { (organizer, accounts[0], "Owner doesn't match!"); done(); // to stop these tests earlier, move this up }).catch(done); }).catch(done); }); });构造函数。

({ from: accounts[0] })通过调⽤合约构造函数创造了⼀个新的Conference实例。由于不指定from时会默认使⽤accounts[0],它其实可以被省略掉:({ from: accounts[0] }); // 和()效果相同Promise。 代码中的那些then和return就是Promise。它们的作⽤写成⼀个深深的嵌套调⽤链的话会是这样:().then( function(num) { (num, 0, "Registrants should be zero!"); ().then( function(organizer) { (organizer, accounts[0], "Owner doesn't match!"); }).then( function(...)) }).then( function(...)) // Because this would Promise减少嵌套,使代码变得扁平,允许调⽤异步返回,并且简化了表达“成功时做这个”和“失败时做那个”的语法。通过实现异步调⽤,因此你不需要等到交易完成就可以继续执⾏前端代码。Truffle借助了⽤Promise封装的⼀个框架,叫做,这个框架本⾝⼜是基于的,它⽀持Promise的⾼级特性。call。 我们使⽤call来检查变量的值,例如().then(...,还可以通过传参数,例如call(0), 来获取mapping在index 0处的元素。Solidity的⽂档说这是⼀种特殊的“消息调⽤”因为 1.不会为矿⼯记录和 2.不需要从钱包账户/地址发起(因此它没有被账户持有者私钥做签名)。另⼀⽅⾯,交易/事务(Transaction)会被矿⼯记录,必须来⾃于⼀个账户(也就是有签名),会被记录到区块链上。对合约中数据做的任何修改都是交易。仅仅是检查⼀个变量的值则不是。因此在读取变量时不要忘记加上call()!否则会发⽣奇怪的事情。(此外如果在读取变量是遇到问题别忘记检查它是否是public。)call()也能⽤于调⽤不是交易的函数。如果⼀个函数本来是交易,但你却⽤call()来调⽤,则不会在区块链上产⽣交易。断⾔。 标准JS测试中的断⾔(如果你不⼩⼼拼成了复数形式’asserts’,truffle会报错,让你⼀头雾⽔),是最常⽤的,其他类型的断⾔可以在中找到。再⼀次运⾏truffle test确保⼀切⼯作正常。测试合约函数调⽤现在我们测试⼀下改变quote变量的函数能⼯作。在tests/⽂件的contract('Conference', function(accounts) {...};)的函数体中添加如下测试⽤例:it("Should update quota", function(done) { var c = (ed_address); ({from: accounts[0] }).then( function(conference) { ().then(

function(quota) {

(quota, 500, "Quota doesn't match!");

}).then( function() {

return Quota(300); }).then( function(result) { // result here is a transaction hash (result); // if you were to print this out it’d be long hex - the transaction hash return () }).then( function(quota) {

(quota, 300, "New quota is not correct!"); done(); }).catch(done); }).catch(done);});这⾥的新东西是调⽤changeQuota函数的那⼀⾏。对于调试很有⽤,⽤它能在运⾏truffle的终端中输出信息。在关键点插⼊可以查看执⾏到了哪⼀步。记得把Solidity合约中changeQuota函数被声明为public,否则你不能调⽤它: function changeQuota(uint newquota) public { }测试交易现在让我们调⽤⼀个需要发起⼈发送资⾦的函数。Wei。 以太币有很多种单位(这⾥有个很有⽤的),在合约中通常⽤的是Wei,最⼩的单位。提供了在各单位与Wei之间互相转换的便利⽅法,形如(.05, 'ether')。JavaScript在处理很⼤的数字时有问题,因此使⽤了,并建议在代码各处都以Wei做单位,直到要给⽤户看的时候(。账户余额。 提供了许多提供⽅便的,其中另⼀个会在下⾯测试⽤到的是ance(some_address)。记住发送给合约的资⾦会由合约⾃⼰持有直到调⽤suicide。在contract(Conference, function(accounts) {...};)的函数体中插⼊下⾯的测试⽤例。在⾼亮显⽰的⽅法中,测试⽤例让另⼀个⽤户(accounts[1])以ticketPrice的价格买了⼀张门票。然后它检查合约的账户余额增加了ticketPrice,以及购票⽤户被加⼊了参会者列表。这个测试中的buyTicket是⼀个交易函数:it("Should let you buy a ticket", function(done) { var c = (ed_address); ({ from: accounts[0] }).then( function(conference) { var ticketPrice = (.05, 'ether'); var initialBalance = ance(s).toNumber(); ket({ from: accounts[1], value: ticketPrice }).then( function() { var newBalance = ance(s).toNumber(); var difference = newBalance - initialBalance; (difference, ticketPrice, "Difference should be what was sent"); return (); }).then(function(num) { (num, 1, "there should be 1 registrant"); return (accounts[1]); }).then(function(amount) { (er(), ticketPrice, "Sender's paid but is not listed"); done(); }).catch(done); }).catch(done);});交易需要签名。 和之前的函数调⽤不同,这个调⽤是⼀个会发送资⾦的交易,在这种情况下购票⽤户(accounts[1])会⽤他的私钥对buyTicket()调⽤做签名。(在geth中⽤户需要在发送资⾦之前通过输⼊密码来批准这个交易或是解锁钱包的账户。)toNumber()。 有时我们需要把Solidity返回的⼗六进制结果转码。如果结果可能是个很⼤的数字可以⽤umber(numberOrHexString)来处理因为JavaScript直接对付⼤数要糟。测试包含转账的合约最后,为了完整性,我们确认⼀下refundTicket⽅法能正常⼯作,⽽且只有会议组织者能调⽤。下⾯是测试⽤例:it("Should issue a refund by owner only", function(done) { var c = (ed_address); ({ from: accounts[0] }).then( function(conference) { var ticketPrice = (.05, 'ether'); var initialBalance = ance(s).toNumber();

ket({ from: accounts[1], value: ticketPrice }).then( function() { var newBalance = ance(s).toNumber(); var difference = newBalance - initialBalance; (difference, ticketPrice, "Difference should be what was sent"); // same as before up to here // Now try to issue refund as second user - should fail return Ticket(accounts[1], ticketPrice, {from: accounts[1]});

}).then( function() { var balance = ance(s).toNumber(); (umber(balance), ticketPrice, "Balance should be unchanged"); // Now try to issue refund as organizer/owner - should work return Ticket(accounts[1], ticketPrice, {from: accounts[0]});

}).then( function() { var postRefundBalance = ance(s).toNumber(); (postRefundBalance, initialBalance, "Balance should be initial balance"); done(); }).catch(done); }).catch(done); });这个测试⽤例覆盖的Solidity函数如下:function refundTicket(address recipient, uint amount) public returns (bool success) { if ( != organizer) { return false; } if (registrantsPaid[recipient] == amount) {

address myAddress = this; if (e >= amount) {

(amount); Refund(recipient, amount); registrantsPaid[recipient] = 0; numRegistrants--; return true; } } return false;}合约中发送以太币。

address myAddress = this展⽰了如何获取该会议合约实例的地址,以变接下来检查这个地址的余额(或者直接使⽤e)。合约通过(amount)⽅法把资⾦发回了购票⼈。交易⽆法返回结果给。 注意这⼀点!refundTicket函数会返回⼀个布尔值,但是这在测试中⽆法检查。因为这个⽅法是⼀个交易函数(会改变合约内数据或是发送以太币的调⽤),⽽得到的交易运⾏结果是⼀个交易哈希(如果打印出来是⼀个长长的⼗六进制/怪怪的字符串)。既然如此为什么还要让refundTicket返回⼀个值?因为在Solidity合约内可以读到这个返回值,例如当另⼀个合约调⽤refundTicket()的时候。也就是说Solidity合约可以读取交易运⾏的返回值,⽽不⾏。另⼀⽅⾯,在中你可以⽤事件机制(Event, 下⽂会解释)来监控交易运⾏,⽽合约不⾏。合约也⽆法通过call()来检查交易是否修改了合约内变量的值。关于sendTransaction()。 当你通过调⽤类似buyTicket()或者refundTicket()的交易函数时(使⽤ansaction),交易并不会⽴即执⾏。事实上交易会被提交到矿⼯⽹络中,交易代码直到其中⼀位矿⼯产⽣⼀个新区块把交易记录进区块链之后才执⾏。因此你必须等交易进⼊区块链并且同步回本地节点之后才能验证交易执⾏的结果。⽤testrpc的时候可能看上去是实时的,因为测试环境很快,但是正式⽹络会⽐较慢。事件/Event。 在中你应该监听⽽不是返回值。我们的智能合约⽰例定义了这些事件:event Deposit(address _from, uint _amount);event Refund(address _to, uint _amount);它们在buyTicket()和refundTicket()中被触发。触发时你可以在testrpc的输出中看到⽇志。要监听事件,你可以使⽤监听器(listener)。在写本⽂时我还不能在truffle测试中记录事件,但是在应⽤中没问题:({ from: accounts[0] }).then( function(conference) { var event = nts().watch({}, ''); // or use t() or .Refund() (function (error, result) { if (error) { ("Error: " + error); } else { ("Event: " + ); } }); // ...过滤器/Filter。 监听所有事件可能会产⽣⼤量的轮询,作为替代可以使⽤过滤器。它们可以更灵活的开始或是停⽌对事件的监听。更多过滤器的信息可查看。总的来说,使⽤事件和过滤器的组合⽐检查变量消耗的Gas更少,因⽽在验证正式⽹络的交易运⾏结果时⾮常有⽤。Gas。 (译注:以太坊上的燃料,因为代码的执⾏必须消耗Gas。直译为汽油⽐较突兀,故保留原⽂做专有名词。)直到现在我们都没有涉及Gas的概念,因为在使⽤testrpc时通常不需要显式的设置。当你转向geth和正式⽹络时会需要。在交易函数调⽤中可以在{from: __, value: __, gas:__}对象内设置Gas参数。提供了调⽤来获取当前Gas的价格,Solidity编译器也提供了⼀个参数让你可以从命令⾏获取合约的Gas开销概要:solc --gas 。下⾯是的结果:为合约创建DApp界⾯下⾯的段落会假设你没有⽹页开发经验。上⾯编写的测试⽤例⽤到的都是在前端界⾯中也可以⽤的⽅法。你可以把前端代码放到app/⽬录中,运⾏truffle build之后它们会和合约配置信息⼀起编译输出到build/⽬录。在开发时可以使⽤truffle watch命令在app/有任何变动时⾃动编译输出到build/⽬录。然后在浏览器中刷新页⾯即可看到build/⽬录中的最新内容。(truffle serve可以启动⼀个基于build/⽬录的⽹页服务器。)app/⽬录中有⼀些样板⽂件帮助你开始:会加载:因此我们只需要添加代码到就可以了。默认的会在浏览器的console(控制台)中输出⼀条”Hello from Truffle!”的⽇志。在项⽬根⽬录中运⾏truffle watch,然后在浏览器中打开build/⽂件,再打开浏览器的console就可以看到。(⼤部分浏览器例如Chrome中,单击右键 -> 选择Inspect Element然后切换到Console即可。)在中,添加⼀个在页⾯加载时会运⾏的调⽤。下⾯的代码会确认已经正常载⼊并显⽰所有可⽤的账户。(注意:你的testrpc节点应该保持运⾏。) = function() { var accounts = ts; (accounts);}看看你的浏览器console中看看是否打印出了⼀组账户地址。现在你可以从tests/中复制⼀些代码过来(去掉只和测试有关的断⾔),将调⽤返回的结果输出到console中以确认代码能⼯作。下⾯是个例⼦: = function() { var accounts = ts; var c = (ed_address); ({ from: accounts[0] }).then( function(conference) { var ticketPrice = (.05, 'ether'); var initialBalance = ance(s).toNumber();

("The conference's initial balance is: " + initialBalance); ket({ from: accounts[1], value: ticketPrice }).then( function() { var newBalance = ance(s).toNumber(); ("After someone bought a ticket it's: " + newBalance); return Ticket(accounts[1], ticketPrice, {from: accounts[0]}); }).then( function() {

var balance = ance(s).toNumber(); ("After a refund it's: " + balance); }); });};上⾯的代码应该输出如下:(console输出的warning信息可忽略。)现在起你就可以使⽤你喜欢的任何前端⼯具,jQuery, ReactJS, Meteor, Ember, AngularJS,等等等等,在app/⽬录中构建可以与以太坊智能合约互动的DApp界⾯了!接下来我们给出⼀个极其简单基于jQuery的界⾯作为⽰例。这⾥是,这⾥是。通过界⾯测试了智能合约之后我意识到最好加⼊检查以保证相同的⽤户不能注册两次。另外由于现在是运⾏在testrpc节点上,速度很快,最好是切换到geth节点并确认交易过程依然能及时响应。否则的话界⾯上就应该显⽰提⽰信息并且在处理交易时禁⽤相关的按钮。尝试geth。 如果你使⽤, 可以尝试以下⾯的命令启动 - 在我这⼉(geth v1.2.3)⼯作的很好:build/bin/geth --rpc --rpcaddr="0.0.0.0" --rpccorsdomain="*" --mine --unlock='0 1' --verbosity=5 --maxpeers=0 --minerthreads='4' --networkid '12345' --genesis 这条命令解锁了两个账户,

0和1。1. 在geth控制台启动后你可能需要输⼊这两个账户的密码。2. 你需要在⽂件⾥⾯的’alloc’配置中加⼊你的这两个账户,并且给它们充⾜的资⾦。3. 最后,在创建合约实例时加上gas参数:({from: accounts[0], gas: 3141592})然后把整个truffle deploy,

truffle build流程重来⼀遍。教程中的代码。 在这篇基础教程中⽤到的所有代码都可以在这个中找到。⾃动为合约⽣成界⾯。 制作了⼀个叫做的⼯具,可以⽤Solidity合约⾃动⽣成HTML, jQuery和的代码。这种模式也正在被越来越多的正在开发中的开发者⼯具采⽤。教程到此结束! 最后⼀章我们仅仅学习了⼀套⼯具集,主要是Truffle和testrpc. 要知道即使在ConsenSys内部,不同的开发者使⽤的⼯具和框架也不尽相同。你可能会发现更适合你的⼯具,这⾥所说的⼯具可能很快也会有改进。但是本⽂介绍的⼯作流程帮助我⾛上了DApp开发之路。(⊙ω⊙) wonk wonk感谢Joseph Chow的校阅和建议,Christian Lundkvist, Daniel Novy, Jim Berry, Peter Borah和Tim Coulter帮我修改⽂字和debug,以及TimCoulter, Nchinda Nchinda和Mike Goldin对DApp前端步骤图提供的帮助。

发布者:admin,转转请注明出处:http://www.yc00.com/news/1688093923a80037.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信