solidity 0.5.7简明教程

以太坊不仅是一种加密数字货币,它更是功能完备的智能合约平台,solidity就是用来开发以太坊上的智能合约的原生开发语言。solidity最早发布于2015年,它是第一种图灵完备的智能合约专用开发语言。目前除了以太坊之外,在其他区块链中也逐渐开始支持solidity,例如hyperledger fabric、tendermint等。在这个solidity快速教程中,我们将使用最新0.5.7版的solidity,以一个具体的案例来介绍solidity智能合约的开发、部署与交互,希望对你快速掌握solidity智能合约的开发有所帮助。

如果要高效系统地掌握以太坊智能合约与DApp的开发,推荐访问汇智网的在线互动课程:

以太坊开发入门 |java以太坊 | python以太坊 | php以太坊 | C#以太坊 | 电商DApp实战 | ERC721通证实战

0、问题的背景

有一个老爷爷,在生命的最后岁月别无他求,只是希望自己的财产能够通过遗嘱顺利地传给其他家庭成员。

在传统的遗嘱中,遗产分配方案是落实在法律文件上的,然后当真正开始分配时,法官需要重审文件并做出相应的决定。常见的问题发生在家庭成员之间对分配比例的争执上,甚至因此而导致家庭成员关系的破裂。在法庭听证阶段,这些都会影响法官最终的裁决,并因此可能导致不公平的结果,甚至对家庭关系造成进一步的伤害。

那么,如果我们可以让遗产分配自动进行,是否可以避免上述情况的发生?

如果遗产是一个智能合约,那么就不需要法官了。老爷爷可以自主地利用合约管理资产,然后在他去世后由程序来分配遗产给家庭成员。合约里的代码就决定了最终的分配结果,因此无需法官的介入。例如萨拉分$10000,本得到$5000,朱丽叶得到$2000。代码执行后,资产以代币或加密货币的形式自动分配给这些家庭成员,而无需人工介入。虽然不能保证每个成员都对遗产的分配结果满意,但是没有人会和代码争执。这听起来还比较可行,对吗?

记住这个案例,在这个快速教程中,我们将使用solidity,为老爷爷开发一个简单的遗嘱合约,来满足他最后的愿望。

1、搭建solidity开发环境

开发solidity智能合约最简单的方法,就是使用官方提供的在线集成开发环境REMIX,你可以点击这里打开remix,在网页里就完成solidity智能合约的编写、编译与部署:

在你打开remix页面后,注意在右侧的run选项页,environment下拉框中,要选中JavaScript VM。这个选项的意思是使用一个内存仿真以太坊节点作为你的solidity智能合约的运行平台,这样就不用考虑与实际的以太坊主网交互所需要的账号、资金、计算费用等问题,而可以先把精力聚焦在学习如何使用solidity表达你的业务逻辑上。

点击remix页面左上方的+图标,就可以创建一个新的代码文件,我们将其命名为will.sol。在remix页面中间的编辑区域可以同时显示多个文件,当前正在编辑的文件,则以活动选项页的形式显示文件名称。

2、声明solidity编译器版本

solidity还是很早期阶段的语言,从语法到编译器都在不断地演化,所以在solidity代码的第一行,一定要用pragma关键字声明这个文件中的solidity代码需要哪个版本的编译器。例如:

注意在solidity中,末尾的分号不可省略。

3、编写第一个solidity合约

接下来就可以定义我们的第一个合约:

使用contract关键字来定义一个合约,solidity的合约类似于我们熟悉的OOP中的类,因此通常合约的名称首字母也会大写,例如Will。一对大括号用来定义合约的实现逻辑,单行注释也使用//,这和很多开发语言都类似。

4、solidity中的全局变量和构造函数

在我们开始写代码之前,应当首先明确遗嘱的条款。假设老爷爷的遗产是50个以太币,其中20个留给他的儿子康莱德,剩下的30个留给他的妻子丽莎。在真实的环境中,当老爷爷去世后,应当有一个外部的程序将调用合约中定义的方法来分配遗产,但是我们为了便于学习将自己完成这个调用。

现在,让我们先完成如下代码:

  • 表征合约所有者的变量
  • 表征遗产数量的变量
  • 表征老爷爷是否还健在的变量
  • 设置上述变量初始值的构造函数

第5行代码定义了合约的所有者。当我们在solidity中定义变量时,必须先声明其类型。address是solidity中一种特殊的类型,它表示一个以太坊地址。address类型的变量有一些特殊的方法,我们在后面会进一步了解。

第6行代码定义的fortune变量用来保存老爷爷的遗产数量,它的类型是uintunsigned int,意思是这个变量是0或正整数。solidity中有很多数据类型,但我们不会在这里一一介绍,你可以在官方文档中深入了解solidity的数据类型。

第7行代码定义的isDeceased变量用来标识老爷爷是否已经去世,这是一个开关量,因此其类型为boolean,可能的值只有两个:true或false,默认值为false。

第9~13行代码是合约的构造函数,这个特殊的函数将在合约部署的时候自动执行。

public关键字被称为可见性修饰符,它的作用是声明被修饰的方法是否允许外部调用。public意味着在合约内部或外部(由其他合约或其他人)都可以调用该方法。

payable关键字是solidity的特色之一,它使得被修饰的方法可以发送或接收以太币。为构造函数声明payable关键字意味着当我们部署合约的时候,可以直接向合约存入以太币,例如,作为遗产的50个以太币。当合约接收到以太币后,这些币就保存在合约地址上了。

在构造函数内部,我们将owner变量的值设置为msg.sender,这是一个以太坊平台预置的全局变量,表示调用合约方法的账号地址,在我们的案例中,这的地址是老爷爷的。

同时我们将fortune变量的值设置为msg.value,这是另一个全局变量,它表示被调用的方法接收到的以太币的数量。

虽然变量isDeceased被自动初始化为默认值false,但为了清晰起见,我们将其显式地设置为false。

5、使用solidity修饰符

在solidity中,修饰符(Modifier)可以为函数附加额外的条件逻辑。例如,假设我有一个用来关灯的方法,同时有一个修饰符要求灯开关必须处于on状态,那么
我们就可以在方法上附加声明这个修饰符,以便确保只有在灯开关处于on状态时,才可以调用这个方法,否则就抛出异常。

第15行代码定义了onlyOwner修饰符。如果一个方法附加声明了这个修饰符,那么就要求调用方法的账号(msg.sender)必须与owner变量的值一致(别忘了我们在构造函数中设置了owner的值)。这个调用条件有助于遗产的分配,我们将在后面看到这一点。

require关键字的意思是,括号里的表达式的值必须为真(true),否则就会抛出异常,不再继续执行代码。

_;起到占位符的作用,在执行过程中,以太坊虚拟机会用被修饰的方法代码来替换它。

第20行代码定义了mustBeDeceased修饰符。如果一个方法附加声明了这个修饰符,那么就只有在isDeceased变量值为true时,才可以调用该方法,否则就抛出异常。

在上面的代码中,我们使用修饰符来限定方法的执行条件,当然也可以不使用修饰符,而直接在方法实现代码中使用require,不过修饰符看起来更高级一些,也更容易实现代码的复用。

6、设定遗产分配方案

现在我们要继续完成遗产在家庭成员之间的分配任务,这需要他们的钱包地址和分配数量。

正如我们之前所述,康莱德将收到20个以太币而丽莎将继承30个。让我们创建一个数组来保存他们的钱包地址,然后写一个方法来分配遗产。

第25行代码定义了一个空数组familyWallets,用来保存所有家庭成员的钱包地址。和其他语言一样,在solidity中数组是顺序存放并且可以使用序号来存取。注意方括号之前的关键字paybale,只有address payable类型的变量,才可以接收以太币,这是0.5版本的solidity与之前版本的区别之一。

第27行代码创建了一个从address类型到uint类型的映射表变量inheritance,用来保存每个钱包地址的遗产数量。这是一个键/值对数据结构,类似于其他语言中的字典或哈希表,可以用键来存取值。

第29行代码定义了一个方法,它的功能是将一个钱包地址添加到familyWallets数组,然后设置该地址在inheritance映射表中的遗产数量。注意附加的onlyOwner修饰符,猜一下为什么我们要在这里声明这个修饰符?

第30行代码将传入方法的钱包地址追加到familyWallets数组的末尾。

第31行代码将传入方法的遗产继承数量设置为映射表inheritance的指定地址(传入方法的另一个参数)的值。

7、实现遗产自动分配

让我们总结一下。到目前为止,我们已经学习了全局变量、数据类型、构造函数、特殊的关键字例如payablepublic、内置的全局变量例如msg.sendermsg.value、修饰符和require、数组、映射表和方法。我们已经搭好了合约的框架,现在让我们把各部分整合起来最终完成合约。

作为这个教程最后一部分的代码,我们将实现家庭成员遗产的自动分配。

第34行定义了payout()方法,注意private关键字,这个可视性修饰符public的反义词,它只允许被修饰的方法在合约内部调用,就像在第42行的代码那样。之所以在这里使用private,主要是考虑到安全性,因为我们不希望任何来自合约外部的调用。注意最后的mustBeDeceased修饰符,目前我们依然不能满足这个修饰符要求的条件来执行payout()方法。

第35行代码是一个for循环,用来遍历familyWallets数组。语法如下:

  • 定义一个计数器变量i,
  • 声明循环的执行条件
  • 每个周期计数器变量i加1

第36行代码是整个合约的核心,我们调用address类型的地址对象的transfer()方法,向该地址转账预定的遗产继承数量,inheritance[familyWallets[i]]表示在inheritance映射表中,键familyWallets[i]的值,也就是第i个家庭成员的遗产继承数量。

第40~42行代码定义了一个方法,当老爷爷去世后将调用这个方法来触发遗产的分配。在这里我们将变量isDeceased的值设置为true。

现在我们完成了吗?

实际上,还不完全是...

这个智能合约的代码是写完了,但是我们怎么用它?现在是收获果实的时候了。

8、solidity合约部署与交互

你的remix页面看起来应该像这样:

在remix页面右边切换到compile选项页,确认按下图选中编译器的版本,然后点击[start to compile]:

你可能会看到静态分析生成的一个蓝色文本框,我们暂时忽略它的提醒,切换到run选项页:

确保Environment下拉框中选中了Javascript VM,点击account的下拉菜单将显示5个测试账户,每个账户都有100个以太币,让我们选择第一个。

向以太坊区块链部署合约并不是免费的,部署者需要支付手续费,通常被称为gas。引入这一机制的目的是避免区块链计算资源被恶意滥用,要进一步了解gas,可以查看这篇文章:1分钟搞清Gas/ Gas Price/ Gas Limit

gas limit字段使用默认值就可以了,我们先不修改它。

value字段表示我们在部署合约时要发送给合约的以太币数量。输入50,还记得我们在定义构造函数时附加的payable关键字吗?

现在继续,点击[deploy]。

你可能立刻会注意到3件事。首先,选中的账户余额现在变成了49.9999… ,这是因为我们转给合约50个以太币,还要扣除一点部署手续费。页面底部的控制台也会提供关于部署过程的详细信息,你可以查看一下。现在看起来是这样:

我们的合约已经成功部署了!它生成了自己的地址,并且显示出我们定义的两个合约方法。作为合约的持有者,我们要做的第一件事,是设置家庭成员的继承数量:康莱德(20)、丽莎(30)。假设我们用account下拉菜单中的第二个作为康莱德的账号,丽莎的用第三个。

选择第二个账号,点击[拷贝到剪切板]图标,然后输入上图中的setInheritance后面的文本输入框。

在我们执行setInheritance方法之前,有几件事情要记住。

传入合约的以太币数量的单位是wei而不是以太币,1 ETH = 1,000,000,000,000,000,000 WEI,这是非常小的单位,因此我们需要将以太币表示的遗产数量先转换为以WEI为单位的值。

在将遗产数量换算后,在将其写入上图中的setInheritance后面的文本输入框中,之前输入的地址后面,这两个值之间注意要用逗号隔开。

还有,别忘了在account下拉框选中第一个账号,还记得onlyOwner修饰符吗?只有合约的持有人才可以调用setInheritance方法!

现在让我们依次为康莱德和丽莎执行setInheritance方法。你应当可以看到控制台输出的成功信息。看一下其中的decoded input

你看,它显示的就是我们输入的数据。

遗产分配好了,但是坏消息来了。老爷爷在73岁时,在一次北极探险中不幸因心脏病突发去世。他总是这么充满激情与活力。

当我们纪念这位老爷爷的同时,我们同时调用遗嘱合约的deceased()方法,完成老爷爷的最后的愿望。。。



原文: solidity 0.5.7简明教程

汇智网翻译整理,转载请标明出处

原文地址:https://blog.51cto.com/xxzhi/2386702

时间: 2024-11-05 04:13:50

solidity 0.5.7简明教程的相关文章

Lisp简明教程

此教程是我花了一点时间和功夫整理出来的,希望能够帮到喜欢Lisp(Common Lisp)的朋友们.本人排版很烂还望多多海涵! <Lisp简明教程>PDF格式下载 <Lisp简明教程>ODT格式下载 具体的内容我已经编辑好了,想下载的朋友可以用上面的链接.本人水平有限,如有疏漏还望之处(要是有谁帮我排排版就好了)还望指出!资料虽然是我整理的,但都是网友的智慧,如果有人需要转载,请至少保留其中的“鸣谢”页(如果能有我就更好了:-)). Lisp简明教程 整理人:Chaobs 邮箱:[

Linux防火墙iptables简明教程

前几天微魔部落再次遭受到个别别有用心的攻击者的攻击,顺便给自己充个电,复习了一下linux下常见的防火墙iptables的一些内容,但是无奈网上的很多教程都较为繁琐,本着简明化学习的目的,微魔为大家剔除了许多冗余的内容,提取出尽量多的精华部分成文,和大家共同学习,本文涉及的内容包括如下 Linux防火墙iptables简明教程 1.安装iptables 2.查看现有的iptables规则 3.删除某iptables规则 4.清除现有iptables规则 5.创建规则 6.设置开机启动 7.保存i

第一课 C语言简明教程

1序言: 1与Java.C#等高级语言相比,C语言却非常简单,学习简单,使用也简单,但是也非常重要,到目前为止基本上操作系统的内核代码超过百分之九十使用C语言完成,因此学好C语言是学好计算机这门课程的基础,特别是进入系统编程尤为明显. 今天是本人复习C语言课程的第一课,主要重新记录一下C语言的基础知识,这节课涉及到C语言的结构.变量以及类型.输入输出.条件判断以及循环知识. 2知识点: 2.1 C语言的结构 2.1.1 通常情况下C语言程序是由: 1.相关的代码注释,使用/* ··· */可注释

Vbs 脚本编程简明教程之一

-为什么要使用 Vbs ? 在 Windows 中,学习计算机操作也许很简单,但是很多计算机工作是重复性劳动,例如你每周也许需要对一些计算机文件进行复制.粘贴.改名.删除,也许你每天启动 计算机第一件事情就是打开 WORD ,切换到你喜爱的输入法进行文本编辑,同时还要播放优美的音乐给工作创造一个舒心的环境,当然也有可能你经常需要对文本中的某 些数据进行整理,把各式各样的数据按照某种规则排列起来--.这些事情重复.琐碎,使人容易疲劳. 第三方软件也许可以强化计算机的某些功能,但是解决这些重复劳动往

Smarty教程1.引擎定义2.主要优点3.简明教程4.使用判断5.循环数组6.常见问题8.解释程序

Smarty是一个php模板引擎.更准确的说,它分开了逻辑程序和外在的内容,提供了一种易于管理的方法.可以描述为应用程序员和美工扮演了不同的角色,因为在大多数情况下 ,他们不可能是同一个人.例如,你正在创建一个用于浏览新闻的网页,新闻标题,标签栏,作者和内容等都是内容要素,他们并不包含应该怎样去呈现.在Smarty的程序里,这些被忽略了.模板设计者们编辑模板,组合使用html标签和模板标签去格式化这些要素的输出(html表格,背景色,字体大小,样式表,等等).有一天程序员想要改变文章检索的方式(

Java泛型简明教程

Java泛型简明教程 博客分类: Java综合 JavaApple数据结构CC++ Java泛型简明教程 本文是从 Java Generics Quick Tutorial 这篇文章翻译而来. 泛型是Java SE 5.0中引入的一项特征,自从这项语言特征出现多年来,我相信,几乎所有的Java程序员不仅听说过,而且使用过它.关于Java泛型的教程,免费的,不免费的,有很多.我遇到的最好的教材有: The Java Tutorial Java Generics and Collections ,

Git简明教程

[git教程] http://www.liaoxuefeng.com/ (廖雪峰博客) http://rogerdudler.github.io/git-guide/index.zh.html (Git简明教程) [常用命令] PS:在MacOS系统环境下的git命令. 工作流 你的本地仓库由 git 维护的三棵"树"组成. > 工作区,它持有实际文件夹. > 暂存区(Stage),它像个缓存区域,临时保存你的改动. > HEAD,它指向你最后一次提交的结果. 配置本

JSP简明教程(五):高级特性

JSP过滤器 过滤器的作用是给web请求增加额外的逻辑,每个页面可以被多个过滤器进行处理.过滤器需要在web.xml文件中进行定义,语法如下.过滤器的执行顺序与filter-mapping的定义顺序相同. <filter> <filter-name>FilterName</filter-name> <filter-class>TestFilter</filter-name> <init-param> <param-name>

JSP简明教程(四):EL表达式语言、JavaBean、Cookie、Session

EL表达式语言 EL就是Expression Language,目的是简化JSP的语法.来看几个例子就明白了. ${test} 会翻译成<%=test%> ${test.name} 会翻译成 <%=test.getName()%> ${sessionScope.username}} 会翻译成 <%=session.getAttribute("username")%> 只有sessionScope.requestScope等才会翻译成getAttrib