智能合约 数据存储怎么储存数据?比如我输入了一组参数,输入之后可以通过查询

从并发视角来看智能合约(上)【渡鸦论文系列】
论文作者:
Ilya Sergey1and Aquinas Hobor2
1 University College London, United Kingdomi.sergey@ucl.ac.uk
2 Yale-NUS College and School of Computing, National University of Singaporehobor@comp.nus.edu.sg
翻译:渡鸦
「让国内外的区块链技术没有时差」。
写在【新栏目】前面:
翻译论文无比痛苦。
但是我们的目标是「让国内外的区块链技术没有时差」。
带给大家更多区块链技术干货。
能力有限,还请各路大神多多监督、指教。
欢迎投稿、中英文均可。
在本文中,我们探讨了诸如Ethereum等智能合约的多事务行为与共享内存并发的经典问题之间的显着相似之处。我们从Ethereum区块链中检查两个真实世界的例子,并分析它们是如何容易受到与传统并发程序中经常出现的错误的影响。然后,我们详细阐述可观察的合约行为与已深入研究的并发主题之间的关系,如原子性,干扰,同步和资源所有权。描述的并发对象类似的类比可以更深入地了解智能合约的潜在威胁,指导实践,并使用现有的最先进的形式验证技术。
智能合约是存储在区块链上,拜占庭容错数据库的程序。智能合约可以通过区块链交易触发,并在其块上读取和写入数据[38] 。虽然智能合约以分布式方式运行和验证,但是尽管存在许多复杂的交互模式,包括例如重入和递归调用,但它们的语义表明可以将它们视为顺序程序。这种心理模型简化了关于合约的正式和非正式推理,使得可以立即重用现有的通用框架来进行程序验证[5,16,31,32] ,可用于验证例如写入的智能合约。固化[15] 只有微小的调整。
虽然区块上的所有计算都是确定的[1],但由于交易本身之间的竞争(如为某个给定的区块选择了哪些交易),所以仍然会发生一些非确定性的计算。我们将展示,非决定论可以被敌对方利用,并对合约行为进行推理,特别微妙,让人想起传统并行编程中涉及的已知挑战。
在本文中,我们概述了智能合约的并发执行属性。这种执行可以跨越多个区块链交易(在同一个块内或多个块中),从而违反了仅使用合约的实施和本地状态而不能指定的所需安全属性,正是现有验证方法所关注的[5, 32] 。为了便于共同编程的重用,我们提出以下类比:
在区块链中使用智能合约的帐户就像在共享内存中使用并发对象的线程。
在共享内存中使用并发对象的线程。通过并发对象,我们意味着用于在同时运行的多个线程(进程)之间交换数据和管理交互的大量数据结构[20] 。并发对象的典型示例是区块,队列和原子计数器 - 通常通过诸如java,util,concurrent的数据库使用。同时,在运行时,这些并发对象被分配到正在运行的线程可访问的共享内存块中。由线程同时访问对象而产生的行为 - 即干扰是难以预测的,因此极难理解。
其不利用适当同步的并发对象(例如,具有锁定或障碍物)可以在干扰下会出现数据竞争[2]行为,导致内存完整性的丧失。即使对于无竞争对象,在一个或多个客户端的观点下,观察到的干扰行为可能是错误的。例如,特定的线程可能不会“预见”具有共享对象的其他线程采取的动作,因此可能不会期望该对象以干扰方式改变。
帐户使用智能合约块。智能合约类似于并发对象。他们储存在块状物中而不是一个共享的记忆中; 不是由线程使用,而是由帐户(用户或其他合约)调用。像并发对象一样,它们具有内部可变状态,管理资源(例如资金),并且可以在块内和多个块中的多方访问。与传统并发对象不同,由于计算的事务模型,智能合约的方法而是原子的。也就是说,合约的单一调用(或一系列调用一系列相互调用的合约)是有序执行 - 没有中断 - 并且在成功更新区块链之后终止或者中止并且回到之前的配置。
然而,“原子性自由”的概念是欺骗性的,因为在区块链的水平上仍然可以观察到并发行为:
- 在交易执行时,包含在交易中的交易的顺序并不确定,因此,结果可以在很大程度上取决于其他交易的排序[27].
- 几个编程任务需要将合约逻辑分散在几个区块链交易中(例如,当合约与块之外的世界进行“通信”时),从而实现真正的并发行为。
- 调用其他合约可以被认为是一种多任务合作。通过协同多任务,多个线程可以运行,但不要中断,除非它们明确地“产生”。也就是说,从合约A到合约B的呼叫可以被认为是从合约A的角度来看的收益,合约B在返回时收益。智能合约的关键在于,合约B可以运行合约A的设计者无意识到的代码,这使得情况比典型的顺序设备更接近并发设置。[3]特别地,合约B可以在调用期间修改合约A可能承担的状态。这是DAO错误[9],的精髓,合约B在返回[27]之前调用合约A来修改A的本地状态。然而,重入并不是表现出来的唯一错误,因为:
- 不难想象某种合约被用作其他区块(用户和合约),管理对共享资源的访问以及在某种意义上作为并发库的场景。随着多重交易变得越来越普遍,各种观察到的干涉模式,也应该考虑在内。
我们的目标和动机。幸运的是,在过去三十年中进行的并行和分布式编程的研究为大量的理论和应用框架提供了代码,指定,理由和正式验证并发对象及其实现的框架。因此,本文的目标是双重的。首先,我们将简要概述在智能合约中可能发生的一些已知的并发问题,在更传统的并发抽象方面表征问题。第二,我们的目标是建立一个直观的“良好”和“不良”的合约行为,可以相应地识别和验证/检测,使用现有的正式方法推理并发。
2.并发行为示例
在这里,我们讨论了已经部署在Ethereum块上的两个合约,每个合并都说明了并发类型行为的不同方面。BlockKing的合约,像今天的Ethereum集团许多其他人一样,实现了一个简单的赌博游戏[2]。虽然BlockKing使用不广泛,但我们研究它是因为它展示了Oraclize服务的潜在用途[4],这是一种允许合约与块之外的世界进行通信的服务,从而了解真正的并发性。由于Oraclize服务的早期采用者将其作为该服务的演示,并将其源代码免费提供,希望使用Oraclize的许多其他合约可能会在其实现中反映出来。
我们讨论的第二个例子是DAO中广泛研究的错误[1]。 DAO与18,000多名投资者建立了一个业主管理的风险投资基金;它吸引了当时存在的14%的以太币。随后的攻击使投资者花费了大约360万Ether,当时价值约5000万美元。 DAO采用了我们所说的“不协调的多任务”,因为当DAO向收件人发送钱时,那个接收者能够运行代码,通过DAO的合约状态来干扰DAO的合约状态,假设DAO在调用期间不会改变。
2.1BlockKing合约
BlockKing的赌博如下。在任何时候,都有一个指定的“块王”(最初是合约的编者)。当发送方将货币发送到合约时,在1到9之间生成随机数j。如果当前块号10等于j,则s成为新的块王。之后,BlockKing在合约中收到了一定比例的资金(根据不同的参数,从50%到90%),合约的编者将收到余额。
在确定性系统中,生成优质随机数是很困难,特别是在所有数据被公开存储的情况下,而且对于攻击者来说,都有经济激励。因此,BlockKing利用信任方WolframAlpha的服务,使用Oraclize服务生成其随机数。假设Oraclize是很好的行为,这个随机数选择的策略应该是攻击者的预测。
BlockKing的代码有365行,但是在图1中给出的代码十分有趣; 这里的行号是由Etherscan[2]给出的合约的实际源代码。当货币发送到合约时,输入函数被调用。它设置一些合约变量(行 299-301)),然后发送查询到Oraclize服务(行 303)。
图1.BlockKing代码片段[2]
oraclize_query函数引发一个事件在“真实世界”中可见,然后返回到其调用者,然后退出(行 304)。在现实世界中,Oraclize服务器监视事件日志,服务请求(在这种情况下通过联系WolframAlpha Web服务),然后在指定的回调点(BlockKing中的行 306 )对原始合约进行新的调用。在事件和其回调之间,可能会发生许多事情,在这种意义上,块调用可以在调用oraclize_query之前推出几个块,并在回调时恢复控制。在此期间,块状态,甚至BlockKing合约本身的状态可能会发生巨大变化。换句话说,这是块上的真正并发行为。
什么可以出错?假设多个赌徒希望在短时间内尝试运气(甚至在同一个区块内)。合约没有尝试追踪这种行为。因此,每个新的参赛者都将覆盖第s 299-301行中的上一个数据(关键warrior块和warrior变量)。当回调最终发生时,批次中的最后一名参赛者将享有多次机会,以赢得该批次中参加其他回调的参赛者的宝座。罪魁祸首是来自process_payment函数的第 339-347 行,称为行309中的回调函数的最后一行。
每次process_支付函数被称为warrior区块的最低有效数字被计算并存储到变量singleDigitBlock中.[4]。每次调用process_支付函数时,他都有一个新机会匹配第339 行中的随机数。如果数字匹配,则最后的参赛者在第345行加冠。
2.2DAO合约
DAO的源代码有1,239行,比BlockKing更复杂[[23]由于这个bug已经写得很多了(例如[9,27])),所以我们仅在图2中给出了关键行。问题是第1012行的顺序,(通过一系列进一步的函数调用)将Ether发送到msg.sender,在1014 行,将零余数的msg.sender的帐户为零。
图.2.DAO代买片段[23]
在顺序程序中,重新排序两个独立的操作对程序的最终行为没有影响。然而,在并发程序中,顺序无害的重新排序的效果可以具有显着的效果,因为操作发生的顺序可以影响线程如何干扰。在DAO中,在第1012 行中发送Ether,在某些多任务的意义上,产生“位于msg.sender的任意(潜在的恶意)合约的控制。
不幸的是,DAO内部状态仍然表明该账户由于其账户余额在第1014 行尚未清零而得到资助。因此,恶意msg.sender可以通过回调DAO合约来启动第二次撤回,该协议将在当控制再次到达1012行时,转一次付款。事实上,恶意的msg.sender可以启动第三,第四等撤回,所有这些将导致付款。只有在支付原始余额的许多倍数之后,最终他的账户被清除。
以前对此bug的分析表明,问题是在于递归或意外的重入。在狭义上,这是真的,但在更广泛的意义上,正在发生的是顺序代码在并发环境中运行的意义。
3干预和同步
3.1共享内存并发中的原子更新
图3描述了错误使用的并发对象的示例(以类似Java8的伪代码呈现),该实例应该使用get和set方法实现“原子”计数器。由于使用了同步原语,左边的并发计数器的实现显然是线程安全的(即无数据竞争)[17] 。然而,有问题的是,在右侧的多线程客户端代码中如何使用Counter类的实例。
图. 3. 并发计数器(左)及其双线程客户端应用程序(右)
具体来说,有两个线程并行运行并且它们的操作分离,对线程2的body内的incr()的调用可能会发生在例如在a中的赋值与a之间的调用c.set(a + 1))调用thread1。这将使以下assert语句中的条件无效,使得整个程序对于某个执行失败!
出现这个问题是因为在计数器之上的incr()的实现没有提供客户端代码预期的原子性保证。具体来说,右边的代码是假设incr()的语句之间不会有任何干扰的,因此计数器c将被增加1,a和b将在也是一样的。实际上,并发的运行线程2并不总是这样,不仅a和b将不同,所以后来的c.set()调用也将“覆盖”早期的结果。
与提供incr()的原子实现相比,更好的Counter的设计实现可以提供通过显式锁定或通过Java的同步关键字,fetch-and-increment操作[20,§5.6]来实施。然而,给定唯一的两种方法是获取和设置,Counter的实现具有一个原子寄存器的同步属性,该原子寄存器的共识号[20,§5.1](即可以明确地同意get和 set)正好是1.因此,而不依赖于一些额外的同步,不给予某些预先设定的线程的优先级,通过仅使用get和set来实现c的原子增量是根本不可能的。
也许有点令人惊讶的是,尽管图3的Counter的实现本身并没有缺陷,但是它的弱原子性质使得它在无限数量的线程的存在下变得无用,使得实际上不可能做出任何稳定的(即,有弹性的关于并发变化)关于其内部状态的假设。
3.2并发区块链交易中的原子更新
图4的左边部分显示了一个在Solidity[15]中实现的智能合约,其功能和方法让人联想到原子并发计数器的功能和方法。函数get允许一个查询与当前余额相关联的合约,与一些固定的地址id相关联,而集合函数可以通过msg.value从消息中获取新的余额来更新余额,发回旧的数量和因此返回。
图.4.计数器合约(左)和同步testAndSet方法(右)
既然获取和设置的两个实体将在一些事务的过程中按顺序执行,那么既不需要同步它们,在Solidity也没有任何明确的方法。然而,不难看出,作为最简单的可能存储(例如,对于某些id相关资金)的实现,由多个不同方使用以更新其平衡,计数器合约与其对应的图中的Java对应,看图3。
例如,假设双方不知道彼此试图增加一个数量的计数器存储的某个值。由于合约不能为他们在一个操作中提供一种方法,所以他们必须首先通过获取查询数量,然后尝试通过设置函数来改变它,遵循与图中实施incr相同的模式。实际上,这两个调用都可以在单个事务中实现。但是由于天然气需求有限,在执行过程中不宜调用多个外部合约。此外,获取的调用可以由块的外部的客户端执行,这意味着获取和设置的连续调用将最终在两个不同的事务中。如果是这种情况,这些调用可能会干扰多方尝试同时修改Counter的其他事务。我们将面对一个常见的问题:不能从本地观察中预测调用函数集的结果。
在共享存储器和块状情况下,问题的原因是缺乏强大的同步原语,允许在同时执行的情况下同时观察和操纵计数器。该问题的一个解决方案是增加计数器的原子可能性,使用testAndSet函数增强计数器(图4的右侧部分)。该功能实现了类似于比较和交换原语[20,第5.8节](在Intelx86和Itanium架构上称为CMPXCHG)的检查/更新逻辑,作为实现多线程之间同步的一种方式。已知testAndSet(和其他一些类似的Read-Modify-Write原语)的一致数量为∞,因此它足够强大,允许任意数量的并发方与操作的结果一致。
关于正式推理和验证的注意事项。运行时并发验证的方法基于探索动态执行跟踪和总结其属性,为检测违反原子性假设和缺乏同步提供了有效的工具[26]。例如,通过将我们的合约转换为相应的共享内存并发对象,可以使用现有工具来总结其跟踪[13],从而可以观察到不需要的交互模式。
周五推送从并发视角来看智能合约(下)
论文的注脚参考资料部分因为含原链接(微信文内无法添加链接),需要的小伙伴可以在渡鸦后台回复 论文获取【论文英文原版】的PDF档(含论文的注脚参考资料及链接)
本文由渡鸦翻译,请联系后台有偿转载,未经授权将追究法律责任。
本文经「原本」原创认证,作者渡鸦区块链,访问yuanben.io查询【2ABMB1V8】获取授权信息
责任编辑:
声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
今日搜狐热点以太坊智能合约编程之菜鸟教程
手把手带你走上智能合约编程之路
译注:首发于ConsenSys开发者博客,原作者为Eva以及ConsenSys的开发团队。如果您想要获取更多及时信息,可以访问首页点击左下角Newsletter订阅邮件。本文的翻译获得了ConsenSys创始人Lubin先生的授权。
有些人说以太坊太难对付,于是我们(译注:指, 下同)写了这篇文章来帮助大家学习如何利用以太坊编写智能合约和应用。这里所用到的工具,钱包,应用程序以及整个生态系统仍处于开发状态,它们将来会更好用!
概述,讨论了关键概念,几大以太坊客户端以及写智能合约用到的编程语言。讨论了总体的工作流程,以及目前流行的一些DApp框架和工具。主要关于编程,我们将学习如何使用Truffle来为智能合约编写测试和构建DApp。第一部分. 概述
如果你对诸如比特币以及其工作原理等密码学货币的概念完全陌生,我们建议你先看看Andreas Antonopoulos所著的的头几章,然后读一下。(译注:以太坊白皮书中文版请看)
如果你觉得白皮书中的章节太晦涩,也可以直接动手来熟悉以太坊。在以太坊上做开发并不要求你理解所有那些“密码经济计算机科学”(crypto economic computer science),而白皮书的大部分是关于以太坊想对于比特币架构上的改进。
提供了官方的新手入门教程,以及一个代币合约和众筹合约的教程。合约语言Solidity也有。学习智能合约的另一份不错的资料(也是我的入门资料)是,不过现在可能有些过时了。
这篇文章的目的是成为上述资料的补充,同时介绍一些基本的开发者工具,使入门以太坊,智能合约以及构建DApps(decentralized apps, 分布式应用)更加容易。我会试图按照我自己(依然是新手)的理解来解释工作流程中的每一步是在做什么,我也得到了ConsenSys酷酷的开发者们的许多帮助。
了解这些名词是一个不错的开始:
公钥加密系统。 Alice有一把公钥和一把私钥。她可以用她的私钥创建数字签名,而Bob可以用她的公钥来验证这个签名确实是用Alice的私钥创建的,也就是说,确实是Alice的签名。当你创建一个以太坊或者比特币钱包的时候,那长长的0xdf...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聊天室或者上问问其他人在用什么版本。
web3.js API. 当Solidity合约编译好并且发送到网络上之后,你可以使用以太坊的来调用它,构建能与之交互的web应用。
以上就是在以太坊上编写智能合约和构建与之交互的DApp所需的基本工具。
第二部分. DApp框架,工具以及工作流程
DApp开发框架
虽然有上文提到的工具就可以进行开发了,但是使用社区大神们创造的框架会让开发更容易。
Truffle and Embark. 是把我领进了门。在Truffle出现之前的那个夏天,我目睹了一帮有天分的学生是如何不眠不休的参加一个hackathon(编程马拉松)活动的,虽然,但我还是吓到了。然后Truffle出现了,帮你处理掉大量无关紧要的小事情,让你可以迅速进入写代码-编译-部署-测试-打包DApp这个流程。另外一个相似的DApp构建与测试框架是。我只用过Truffle,
但是两个阵营都拥有不少DApp大神。
Meteor. 许多DApp开发者使用的另一套开发栈由web3.js和组成,Meteor是一套通用webapp开发框架(项目提供了一个很棒的入门实例,而正在构建大量Meteor与web3.js和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将支持沙盒测试网络,自动生成用于测试的用户界面(取代后文将展示的手动编写测试),以及一个测试交易浏览器。当你的合约准备正式上线之前,使用他们的测试网络会是确保你的智能合约在一个接近真实的环境工作正常的好方法。他们也为正式网络提供了一个交易浏览器,上面可以看到每一笔交易的细节。在本文写作时Ether.Camp的IDE还只能通过邀请注册,预计很快会正式发布。
合约和Dapp示例。 在Github上搜索DApp仓库和.sol文件可以看到进行中的有趣东西。这里有一个DApp大列表:,不过其中一些项目已经过时。上有一些Solidity和Serpent写的合约示例,但是不清楚这些例子有没有经过测试或者正确性验证。11月12日的将会有一整天的DApp主题演讲。
部署智能合约的流程
流程如下:
启动一个以太坊节点 (例如geth或者testrpc)。使用solc*编译*智能合约。 =& 获得二进制代码。将编译好的合约部署到网络。(这一步会消耗以太币,还需要使用你的节点的默认地址或者指定地址来给合约签名。) =& 获得合约的区块链地址和ABI(合约接口的JSON表示,包括变量,事件和可以调用的方法)。(译注:作者在这里把ABI与合约接口弄混了。ABI是合约接口的二进制表示。)用web3.js提供的JavaScript API来调用合约。(根据调用的类型有可能会消耗以太币。)下图详细描绘了这个流程:
你的DApp可以给用户提供一个界面先部署所需合约再使用之(如图1到4步),也可以假设合约已经部署了(常见方法),直接从使用合约(如图第6步)的界面开始。
第三部分. 编程
在Truffle中进行测试
用来做智能合约的测试驱动开发(TDD)非常棒,我强烈推荐你在学习中使用它。它也是学习使用JavaScript Promise的一个好途径,例如deferred和异步调用。Promise机制有点像是说“做这件事,如果结果是这样,做甲,如果结果是那样,做乙... 与此同时不要在那儿干等着结果返回,行不?”。Truffle使用了包装web3.js的一个JS
Promise框架(因此它为为你安装web3.js)。(译注: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 YourContract.sol),truffle deploy来编译和部署合约,最后是truffle test来运行智能合约的测试用例。
第一个合约
下面是一个针对会议的智能合约,通过它参会者可以买票,组织者可以设置参会人数上限,以及退款策略。本文涉及的所有代码都可以在这个找到。
contract Conference {
mapping (address =& uint) public registrantsP
uint public numR
event Deposit(address _from, uint _amount);
// so you can log these events
event Refund(address _to, uint _amount);
function Conference() { // Constructor
organizer = msg.
quota = 500;
numRegistrants = 0;
function buyTicket() public returns (bool success) {
if (numRegistrants &= quota) { }
registrantsPaid[msg.sender] = msg.
numRegistrants++;
Deposit(msg.sender, msg.value);
function changeQuota(uint newquota) public {
if (msg.sender != organizer) { }
function refundTicket(address recipient, uint amount) public {
if (msg.sender != organizer) { }
if (registrantsPaid[recipient] == amount) {
address myAddress =
if (myAddress.balance &= amount) {
recipient.send(amount);
registrantsPaid[recipient] = 0;
numRegistrants--;
Refund(recipient, amount);
function destroy() { // so funds not locked in contract forever
if (msg.sender == 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/YourContractName.sol文件。
3. 把合约名字加到config/app.json的'contracts'部分。
4. 启动以太坊节点(例如在另一个终端里面运行testrpc)。
5. truffle deploy(在truffle项目目录中)
添加一个智能合约。 在truffle init执行后或是一个现有的项目目录中,复制粘帖上面的会议合约到contracts/Conference.sol文件中。然后打开config/app.json文件,把'Conference'加入'deploy'数组中。
启动testrpc。 在另一个终端中启动testrpc。
编译或部署。 执行truffle compile看一下合约是否能成功编译,或者直接truffle deploy一步完成编译和部署。这条命令会把部署好的合约的地址和ABI(应用接口)加入到配置文件中,这样之后的truffle test和truffle build步骤可以使用这些信息。
出错了? 编译是否成功了?记住,错误信息即可能出现在testrpc终端也可能出现在truffle终端。
重启节点后记得重新部署! 如果你停止了testrpc节点,下一次使用任何合约之前切记使用truffle deploy重新部署。testrpc在每一次重启之后都会回到完全空白的状态。
合约代码解读
让我们从智能合约头部的变量声明开始:
mapping (address =& uint) public registrantsP
uint public numR
address. 地址类型。第一个变量是会议组织者的钱包地址。这个地址会在合约的构造函数function Conference()中被赋值。很多时候也称呼这种地址为'owner'(所有人)。
uint. 无符号整型。区块链上的存储空间很紧张,保持数据尽可能的小。
public. 这个关键字表明变量可以被合约之外的对象使用。private修饰符则表示变量只能被本合约(或者衍生合约)内的对象使用。如果你想要在测试中通过web3.js使用合约中的某个变量,记得把它声明为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 =
Suicide, Solidity的好东西。(译注:suicide意为'自杀', 为Solidity提供的关键字,不做翻译。)转给合约的资金会保存于合约(地址)中。最终这些资金通过destroy函数被释放给了构造函数中设置的组织者地址。这是通过suicide(orgnizer);这行代码实现的。没有这个,资金可能被永远锁定在合约之中(reddit上有些人就遇到过),因此如果你的合约会接受资金一定要记得在合约中使用这个方法!
如果想要模拟另一个用户或者对手方(例如你是卖家想要模拟一个买家),你可以使用可用地址数组中另外的地址。假设你要以另一个用户,accounts[1], 的身份来买票,可以通过from参数设置:
conference.buyTicket({ from: accounts[1], value: some_ticket_price_integer });
函数调用可以是交易。 改变合约状态(修改变量值,添加记录,等等)的函数调用本身也是转账交易,隐式的包含了发送人和交易价值。因此web3.js的函数调用可以通过指定{ from: __, value: __ }参数来发送以太币。在Solidity合约中,你可以通过msg.sender和msg.value来获取这些信息:
function buyTicket() public {
registrantsPaid[msg.sender] = msg.
事件(Event)。 可选的功能。合约中的Deposit(充值)和Send(发送)事件是会被记录在以太坊虚拟机日志中的数据。它们实际上没有任何作用,但是用事件(Event)把交易记录进日志是好的做法。
好了,现在让我们给这个智能合约写一个测试,来确保它能工作。
把项目目录test/中的example.js文件重命名为conference.js,文件中所有的'Example'替换为'Conference'。
contract('Conference', function(accounts) {
it("should assert true", function(done) {
var conference = Conference.at(Conference.deployed_address);
assert.isTrue(true);
// stops tests at this point
在项目根目录下运行truffle test,你应该看到测试通过。在上面的测试中truffle通过Conference.deployed_address获得合约部署在区块链上的地址。
让我们写一个测试来初始化一个新的Conference,然后检查变量都正确赋值了。将conference.js中的测试代码替换为:
contract('Conference', function(accounts) {
it("Initial conference settings should match", function(done) {
var conference = Conference.at(Conference.deployed_address);
// same as previous example up to here
Conference.new({ from: accounts[0]
.then(function(conference) {
conference.quota.call().then(
function(quota) {
assert.equal(quota, 500, "Quota doesn't match!");
}).then( function() {
return conference.numRegistrants.call();
}).then( function(num) {
assert.equal(num, 0, "Registrants should be zero!");
return conference.organizer.call();
}).then( function(organizer) {
assert.equal(organizer, accounts[0], "Owner doesn't match!");
// to stop these tests earlier, move this up
}).catch(done);
}).catch(done);
构造函数。 Conference.new({ from: accounts[0] })通过调用合约构造函数创造了一个新的Conference实例。由于不指定from时会默认使用accounts[0],它其实可以被省略掉:
Conference.new({ from: accounts[0] }); // 和Conference.new()效果相同
Promise. 代码中的那些then和return就是Promise。它们的作用写成一个深深的嵌套调用链的话会是这样:
conference.numRegistrants.call().then(
function(num) {
assert.equal(num, 0, "Registrants should be zero!");
conference.organizer.call().then(
function(organizer) {
assert.equal(organizer, accounts[0], "Owner doesn't match!");
function(...))
function(...))
// Because this would get hairy...
Promise减少嵌套,使代码变得扁平,允许调用异步返回,并且简化了表达“成功时做这个”和“失败时做那个”的语法。Web3.js通过实现异步调用,因此你不需要等到交易完成就可以继续执行前端代码。Truffle借助了用Promise封装web3.js的一个框架,叫做,这个框架本身又是基于的,它支持Promise的高级特性。
call. 我们使用call来检查变量的值,例如conference.quota.call().then(...,还可以通过传参数,例如call(0), 来获取mapping在index 0处的元素。Solidity的文档说这是一种特殊的“消息调用”因为 1.不会为矿工记录和 2.不需要从钱包账户/地址发起(因此它没有被账户持有者私钥做签名)。另一方面,交易/事务(Transaction)会被矿工记录,必须来自于一个账户(也就是有签名),会被记录到区块链上。对合约中数据做的任何修改都是交易。仅仅是检查一个变量的值则不是。因此在读取变量时不要忘记加上call()!否则会发生奇怪的事情。(此外如果在读取变量是遇到问题别忘记检查它是否是public。)call()也能用于调用不是交易的函数。如果一个函数本来是交易,但你却用call()来调用,则不会在区块链上产生交易。
断言。 标准JS测试中的断言(如果你不小心拼成了复数形式'asserts',truffle会报错,让你一头雾水),assert.equal是最常用的,其他类型的断言可以在中找到。
再一次运行truffle test确保一切工作正常。
测试合约函数调用
现在我们测试一下改变quote变量的函数能工作。在tests/conference.js文件的contract('Conference', function(accounts) {...};)的函数体中添加如下测试用例:
it("Should update quota", function(done) {
var c = Conference.at(Conference.deployed_address);
Conference.new({from: accounts[0] }).then(
function(conference) {
conference.quota.call().then(
function(quota) {
assert.equal(quota, 500, "Quota doesn't match!");
}).then( function() {
return conference.changeQuota(300);
}).then( function(result) {
// result here is a transaction hash
console.log(result);
// if you were to print this out it’d be long hex - the transaction hash
return conference.quota.call()
}).then( function(quota) {
assert.equal(quota, 300, "New quota is not correct!");
}).catch(done);
}).catch(done);
这里的新东西是调用changeQuota函数的那一行。console.log对于调试很有用,用它能在运行truffle的终端中输出信息。在关键点插入console.log可以查看执行到了哪一步。记得把Solidity合约中changeQuota函数被声明为public,否则你不能调用它:
function changeQuota(uint newquota) public {
现在让我们调用一个需要发起人发送资金的函数。
Wei. 以太币有很多种单位(这里有个很有用的),在合约中通常用的是Wei,最小的单位。Web3.js提供了在各单位与Wei之间互相转换的便利方法,形如web3.toWei(.05, 'ether')。JavaScript在处理很大的数字时有问题,因此web3.js使用了,并建议在代码各处都以Wei做单位,直到要给用户看的时候(。
账户余额。 Web3.js提供了许多提供方便的,其中另一个会在下面测试用到的是web3.eth.getBalance(some_address)。记住发送给合约的资金会由合约自己持有直到调用suicide。
在contract(Conference, function(accounts) {...};)的函数体中插入下面的测试用例。在高亮显示的方法中,测试用例让另一个用户(accounts[1])以ticketPrice的价格买了一张门票。然后它检查合约的账户余额增加了ticketPrice,以及购票用户被加入了参会者列表。
这个测试中的buyTicket是一个交易函数:
it("Should let you buy a ticket", function(done) {
var c = Conference.at(Conference.deployed_address);
Conference.new({ from: accounts[0] }).then(
function(conference) {
var ticketPrice = web3.toWei(.05, 'ether');
var initialBalance = web3.eth.getBalance(conference.address).toNumber();
conference.buyTicket({ from: accounts[1], value: ticketPrice }).then(
function() {
var newBalance = web3.eth.getBalance(conference.address).toNumber();
var difference = newBalance - initialBalance;
assert.equal(difference, ticketPrice, "Difference should be what was sent");
return conference.numRegistrants.call();
}).then(function(num) {
assert.equal(num, 1, "there should be 1 registrant");
return conference.registrantsPaid.call(accounts[1]);
}).then(function(amount) {
assert.equal(amount.toNumber(), ticketPrice, "Sender's paid but is not listed");
}).catch(done);
}).catch(done);
交易需要签名。 和之前的函数调用不同,这个调用是一个会发送资金的交易,在这种情况下购票用户(accounts[1])会用他的私钥对buyTicket()调用做签名。(在geth中用户需要在发送资金之前通过输入密码来批准这个交易或是解锁钱包的账户。)
toNumber(). 有时我们需要把Solidity返回的十六进制结果转码。如果结果可能是个很大的数字可以用web3.toBigNumber(numberOrHexString)来处理因为JavaScript直接对付大数要糟。
测试包含转账的合约
最后,为了完整性,我们确认一下refundTicket方法能正常工作,而且只有会议组织者能调用。下面是测试用例:
it("Should issue a refund by owner only", function(done) {
var c = Conference.at(Conference.deployed_address);
Conference.new({ from: accounts[0] }).then(
function(conference) {
var ticketPrice = web3.toWei(.05, 'ether');
var initialBalance = web3.eth.getBalance(conference.address).toNumber();
conference.buyTicket({ from: accounts[1], value: ticketPrice }).then(
function() {
var newBalance = web3.eth.getBalance(conference.address).toNumber();
var difference = newBalance - initialBalance;
assert.equal(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 conference.refundTicket(accounts[1], ticketPrice, {from: accounts[1]});
function() {
var balance = web3.eth.getBalance(conference.address).toNumber();
assert.equal(web3.toBigNumber(balance), ticketPrice, "Balance should be unchanged");
// Now try to issue refund as organizer/owner - should work
return conference.refundTicket(accounts[1], ticketPrice, {from: accounts[0]});
function() {
var postRefundBalance = web3.eth.getBalance(conference.address).toNumber();
assert.equal(postRefundBalance, initialBalance, "Balance should be initial balance");
}).catch(done);
}).catch(done);
这个测试用例覆盖的Solidity函数如下:
function refundTicket(address recipient, uint amount) public returns (bool success) {
if (msg.sender != organizer) { }
if (registrantsPaid[recipient] == amount) {
address myAddress =
if (myAddress.balance &= amount) {
recipient.send(amount);
Refund(recipient, amount);
registrantsPaid[recipient] = 0;
numRegistrants--;
合约中发送以太币。 address myAddress = this展示了如何获取该会议合约实例的地址,以变接下来检查这个地址的余额(或者直接使用this.balance)。合约通过recipient.send(amount)方法把资金发回了购票人。
交易无法返回结果给web3.js. 注意这一点!refundTicket函数会返回一个布尔值,但是这在测试中无法检查。因为这个方法是一个交易函数(会改变合约内数据或是发送以太币的调用),而web3.js得到的交易运行结果是一个交易哈希(如果打印出来是一个长长的十六进制/怪怪的字符串)。既然如此为什么还要让refundTicket返回一个值?因为在Solidity合约内可以读到这个返回值,例如当另一个合约调用refundTicket()的时候。也就是说Solidity合约可以读取交易运行的返回值,而web3.js不行。另一方面,在web3.js中你可以用事件机制(Event,
下文会解释)来监控交易运行,而合约不行。合约也无法通过call()来检查交易是否修改了合约内变量的值。
关于sendTransaction(). 当你通过web3.js调用类似buyTicket()或者refundTicket()的交易函数时(使用web3.eth.sendTransaction),交易并不会立即执行。事实上交易会被提交到矿工网络中,交易代码直到其中一位矿工产生一个新区块把交易记录进区块链之后才执行。因此你必须等交易进入区块链并且同步回本地节点之后才能验证交易执行的结果。用testrpc的时候可能看上去是实时的,因为测试环境很快,但是正式网络会比较慢。
事件/Event. 在web3.js中你应该监听而不是返回值。我们的智能合约示例定义了这些事件:
event Deposit(address _from, uint _amount);
event Refund(address _to, uint _amount);
它们在buyTicket()和refundTicket()中被触发。触发时你可以在testrpc的输出中看到日志。要监听事件,你可以使用web.js监听器(listener)。在写本文时我还不能在truffle测试中记录事件,但是在应用中没问题:
Conference.new({ from: accounts[0] }).then(
function(conference) {
var event = conference.allEvents().watch({}, ''); // or use conference.Deposit() or .Refund()
event.watch(function (error, result) {
if (error) {
console.log("Error: " + error);
console.log("Event: " + result.event);
过滤器/Filter. 监听所有事件可能会产生大量的轮询,作为替代可以使用过滤器。它们可以更灵活的开始或是停止对事件的监听。更多过滤器的信息可查看。
总的来说,使用事件和过滤器的组合比检查变量消耗的Gas更少,因而在验证正式网络的交易运行结果时非常有用。
Gas. (译注:以太坊上的燃料,因为代码的执行必须消耗Gas。直译为汽油比较突兀,故保留原文做专有名词。)直到现在我们都没有涉及Gas的概念,因为在使用testrpc时通常不需要显式的设置。当你转向geth和正式网络时会需要。在交易函数调用中可以在{from: __, value: __, gas: __}对象内设置Gas参数。Web3.js提供了调用来获取当前Gas的价格,Solidity编译器也提供了一个参数让你可以从命令行获取合约的Gas开销概要:solc
--gas YouContract.sol。下面是Conference.sol的结果:
为合约创建DApp界面
下面的段落会假设你没有网页开发经验。
上面编写的测试用例用到的都是在前端界面中也可以用的方法。你可以把前端代码放到app/目录中,运行truffle build之后它们会和合约配置信息一起编译输出到build/目录。在开发时可以使用truffle watch命令在app/有任何变动时自动编译输出到build/目录。然后在浏览器中刷新页面即可看到build/目录中的最新内容。(truffle
serve可以启动一个基于build/目录的网页服务器。)
app/目录中有一些样板文件帮助你开始:
index.html会加载app.js:
因此我们只需要添加代码到app.js就可以了。
默认的app.js会在浏览器的console(控制台)中输出一条"Hello from Truffle!"的日志。在项目根目录中运行truffle watch,然后在浏览器中打开build/index.html文件,再打开浏览器的console就可以看到。(大部分浏览器例如Chrome中,单击右键 -& 选择Inspect Element然后切换到Console即可。)
在app.js中,添加一个在页面加载时会运行的window.onload调用。下面的代码会确认web3.js已经正常载入并显示所有可用的账户。(注意:你的testrpc节点应该保持运行。)
window.onload = function() {
var accounts = web3.eth.accounts;
console.log(accounts);
看看你的浏览器console中看看是否打印出了一组账户地址。
现在你可以从tests/conference.js中复制一些代码过来(去掉只和测试有关的断言),将调用返回的结果输出到console中以确认代码能工作。下面是个例子:
window.onload = function() {
var accounts = web3.eth.accounts;
var c = Conference.at(Conference.deployed_address);
Conference.new({ from: accounts[0] }).then(
function(conference) {
var ticketPrice = web3.toWei(.05, 'ether');
var initialBalance = web3.eth.getBalance(conference.address).toNumber();
console.log("The conference's initial balance is: " + initialBalance);
conference.buyTicket({ from: accounts[1], value: ticketPrice }).then(
function() {
var newBalance = web3.eth.getBalance(conference.address).toNumber();
console.log("After someone bought a ticket it's: " + newBalance);
return conference.refundTicket(accounts[1], ticketPrice, {from: accounts[0]});
function() {
var balance = web3.eth.getBalance(conference.address).toNumber();
console.log("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 test-genesis.json
这条命令解锁了两个账户, 0和1。1. 在geth控制台启动后你可能需要输入这两个账户的密码。2. 你需要在文件里面的'alloc'配置中加入你的这两个账户,并且给它们充足的资金。3.
最后,在创建合约实例时加上gas参数:
Conference.new({from: accounts[0], gas: 3141592})
然后把整个truffle deploy, truffle build流程重来一遍。
教程中的代码。 在这篇基础教程中用到的所有代码都可以在这个中找到。
自动为合约生成界面。 制作了一个叫做的工具,可以用Solidity合约自动生成HTML, jQuery和web.js的代码。这种模式也正在被越来越多的正在开发中的开发者工具采用。
教程到此结束! 最后一章我们仅仅学习了一套工具集,主要是Truffle和testrpc. 要知道即使在ConsenSys内部,不同的开发者使用的工具和框架也不尽相同。你可能会发现更适合你的工具,这里所说的工具可能很快也会有改进。但是本文介绍的工作流程帮助我走上了DApp开发之路。
(⊙ω⊙) wonk wonk
感谢Joseph Chow的校阅和建议,Christian Lundkvist, Daniel Novy, Jim Berry, Peter Borah和Tim Coulter帮我修改文字和debug,以及Tim Coulter, Nchinda Nchinda和Mike Goldin对DApp前端步骤图提供的帮助。
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!}

我要回帖

更多关于 以太坊 智能合约 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信