区块链学习笔记之以太坊(三)

二、以太坊

9. 以太坊(ETH)智能合约

本章节我们来介绍一下智能合约,智能合约是以太坊的精髓。

9.1 智能合约简介

(1)智能合约的本质:运行在区块链系统上的一段代码,这个代码的逻辑就定义了合约内容。智能合约的账户保存了合约当前的运行状态,合约的状态包括以下几项:

  • balance:当前余额
  • nonce:交易次数
  • code:合约代码
  • storage:存储,数据结构为一棵MPT

(2)Solidity是智能合约最常用的语言,其语法与JavaScript很接近。下图显示了智能合约的代码结构。

  • 第一行声明一下所使用的Solidity的版本号不同版本的Solidity在语法上有一些小的差别。Solidity是面向对象的编程语言,这里的contract类似于c++中的类(class)。
  • 这里的contract定义了很多状态变量。Solidity是强类型语言(strongly typed),这里的变量类型大部分和c++之类的是比较接近的,比如uint类型是unsigned int,也就是无符号整数。address类型是Solidity语言所特有的。后面会细说一下地址类型的成员变量和成员函数。
  • 接下来是两个event(事件)。这个事件的作用是用来记录日志的,也就是说用来打log。第一个事件是HighestBidIncrease(),即拍卖的最高出价增加了。声明一下,我们所使用的代码示例是网上拍卖的一个例子,拍卖的具体规则,后面细说。
  • 让我们接着看这个事件,如果某个人出一个新的最高价,那么我们就记录一下参数bidder(投标人的地址),金额是amount。第二个事件是Pay2Beneficiary(),它的参数是赢得拍卖人的地址winner以及他最后的出价amount。
  • Solidity语言和普通的编程语言相比,它有一些特别之处,比如下图中的mapping,我们可以看出mapping是一个哈希表,保存了一个地址到unsigned int的一个映射。Solidity语言中的哈希表的一个比较奇怪的地方是它不支持遍历,如果你想遍历哈希表中的所有元素,那么你自己需要想个办法记录一下哈希表中有哪些元素。我们这里用的bidders数组来记录的。
  • Solidity语言中的数组可以是固定长度的,也可以是动态改变长度的。那么我们这里是一个动态改变长度的数据,如果你想往这个数组里面增加一个元素那就用push操作,bidders.push(bidder),就是在数组末尾新增一个出价人。如果你想知道数组有多少个元素,可以用它的bidders.length()。如果数组是一个固定长度的话,那么需要写明数组的长度,如address [1024] bidders。
  • 接着看下面的代码,接下来是它的构造函数,Solidity语言中定义构造函数的方法有两种。一种方法就像c++中断构造函数一样,定义一个与contract同名的函数,这个函数可以有参数但是不能有返回值。新版本的Solidity语言实际上更推荐我们我们图中代码的方法,用constructor来定义一个构造函数,那么这个构造函数只有在合约创建的时候会被调用一次。
  • 再下面是三个成员函数,这三个函数都是public,说明其他函数可以调用这些函数。我们注意一下这个bid()函数,这里有一个标志叫做payable,等会后面细说。还要注意一点,构造函数只能有一个。

在这里插入图片描述

9.2 智能合约的调用

(1)下面我们说一下,如何调用智能合约。

  • 调用智能合约其实就是和转账是类似的。比如A发起一个交易转账给B,如果B是一个普通账户,那就和比特币中的转账交易是一样的。如果B是一个合约账户,那么这个转账实际上是发起一次对B合约的调用。
  • 那么具体调用的是合约中的哪个函数呢?
    这是在数据域中说明的,就是这个data域中说明。下图实例中,sender address 是发起调用的账户的地址,to contract address是被调用的合约的地址,调用的函数就是transaction data(图中红框标明的)。如果这个函数有参数,那么参数值也是在data域中说明的。
    下图中中间这一行是调用的参数,value是说,我发起调用合约时,转过去多少钱。这里是0,因为我这个调用的目的仅仅是为了调用它的函数,并非是真的要转账。
    GAS USED是我交易花了多少汽油费,GAS PRICE是单位汽油的价格,GAS LIMIT是这个交易我最多愿意支付多少汽油费,关于汽油费,后面细说。

在这里插入图片描述

(2)除了外部账户可以调用一个合约函数之外,一个合约也可以调用另外一个合约函数,那么调用的方法有如下:

  • 第一种方法,如下图,
  1. 我们叫做直接调用,就是由A和B两个合约,A这个合约只是写一条log,event是定义一个事件LogCallFoo(),emit LogCallFoo()就是用emit操作来调用这个事件。emit语句的作用就是写一个log,对于程序的运行逻辑是没有影响的。
  2. 那么再看看B这个合约,这个合约的函数的参数是一个地址,就是A这个合约的地址,然后这个语句把这个地址转换成A合约的一个实例,然后调用其中的foo()函数。
  3. 注意:以太坊中规定,一个交易只有外部账户才能发起,合约账户不能够自己主动发起一个交易,所以下图例子中是需要有一个外部账户调用了合约B当中的函数callAFooDirectly(),然后这个函数再调用A当中的foo()函数。

在这里插入图片描述

  • 第二种调用方法如下图,用地址类型,用address的call函数。
  1. 这时候第一个参数是要调用那个函数的签名,然后后面跟的是调用的参数。
  2. 这种调用方法和直接调用方法相比一个区别是对错误处理的不同。直接调用方法,如果你调用的合约在执行过程中出现了错误,那么会导致发起调用的合约也跟着一起回滚。
  3. 比如直接调用的图例中,A在执行过程中出现了异常&
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值