以太坊不仅是一种加密数字货币,它更是功能完备的智能合约平台,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智能合约的编写、编译与部署:

solidity ide remix

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

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

2、声明solidity编译器版本

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

solidity ide remix

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

3、编写第一个solidity合约

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

solidity ide remix

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

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

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

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

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

solidity ide remix

第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状态时,才可以调用这个方法,否则就抛出异常。

solidity ide remix

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

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

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

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

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

6、设定遗产分配方案

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

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

solidity ide remix

第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、数组、映射表和方法。我们已经搭好了合约的框架,现在让我们把各部分整合起来最终完成合约。

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

solidity ide remix

第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页面看起来应该像这样:

solidity ide remix

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

solidity ide remix

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

solidity ide remix

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

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

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

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

现在继续,点击[deploy]。

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

solidity ide remix

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

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

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

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

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

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

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

solidity ide remix

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

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

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


原文: solidity 0.5.7简明教程

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


tualala
264 声望28 粉丝

java比特币开发详解: