ETH_22

北京大学肖臻老师《区块链技术与应用》智能合约

背景

智能合约:运行在区块链系统上的一段代码,代码逻辑定义了合约内容。
智能合约的账户保存了合约当前的运行状态:
balance:当前余额
nonce:交易次数
code:合约代码
storage:存储,数据结构为一棵MPT
智能合约编写代码为Solidity,其语法与JavaScript很接近(一点也不像)。
下图显示了智能合约的代码结构
在这里插入图片描述

账户调用

只有外部账户才能调用合约账户,合约账户不能主动发起交易
在这里插入图片描述

合约之间 调用方式

  1. 直接调用
    在这里插入图片描述

错误处理:直接调用的方式,一方产生异常会导致另一方也进行回滚操作。

  1. call 调用
    在这里插入图片描述

错误处理:address.call()的方法,如果调用过程中被调用合约产生异常,会导致call()返回false,但发起调用的函数不会抛出异常,而是继续执行。

  1. 代理调用
    在这里插入图片描述

和call()调用基本一致,区别在于其并不会切入被调用合约的上下文中。

Payable

如下,成员函数中的第一个函数,有一个payable修饰。原因是以太坊中规定,如果一个函数可以接收外部转账,则必须标记为payable。该例中背景为拍卖,bid()为出价,因此需要payable进行标记;withdraw()为其他未拍卖到的人将锁定在智能合约中的钱取出的函数,其不涉及转账,因此不需要payable进行标记。
在这里插入图片描述

fallback()函数

在这里插入图片描述
该函数主要是防止A向B转账,但没有在data域中说明要调用哪个函数或说明的要调用函数不存在,此时调用fallback()函数。
只有合约账户才有代码,因此这些只和合约账户有关。如果没有fallback(),在发生之前的情况后,就会直接抛出异常。
另:转账金额和汽油费是不同的。汽油费是为了让矿工打包该交易,而转账金额是单纯为了转账,其可以为0,但汽油费必须给。

智能合约创建与运行

EVM设计思想类似于JAVA中的JVM,便于跨平台增强可移植性。EVM中寻址空间256位,而目前个人机主流位32位和64位,与之存在较大差距
在这里插入图片描述

gas

以太坊中功能很充足,提供图灵完备的平台,从而使得以太坊相对于比特币可以实现很多功能,但这也导致一些问题,例如当一个全节点收到一个对智能合约调用怎么知晓其是否会导致死循环。
事实上,无法预知其是否会导致死循环,实际上,该问题是一个停机问题,而停机问题不可解。因此,以太坊引入汽油费机制将该问题扔给了发起交易的账户。
以太坊规定,执行合约中指令需要收取汽油费,并且由发起交易的人进行支付。
在这里插入图片描述
当一个全节点收到一个对智能合约的调用,先按照最大汽油费收取,从其账户一次性扣除,再根据实际执行情况,多退少补(汽油费不够会引发回滚,而非简单的补齐)。

以太坊中存在gaslimit,通过收取汽油费保障系统中不会存在对资源消耗特别大的调用。但与比特币不同,比特币直接通过限制区块大小1MB保障对网络资源压力不会过大,这1MB大小是固定的,无法修改。而以太坊中,每个矿工都可以以前一个区块中gaslimt为基数,进行上调或下调1/1024,从而,通过绝大多数区块不断上下调整,保证得到一个较为理想化的gaslimt值(感觉这里有些类似于众包机制)。最终整个系统的gaslimt就是所有矿工希望的平均值。

为什么要引入汽油费?
在比特币系统中,交易是比较简单的,仅仅是转账操作,也就是说可以通过交易的字节数衡量出交易所需要消耗的资源多少。但以太坊中引入了智能合约,而智能合约逻辑很复杂,其字节数与消耗资源数并无关联。存在某些交易,从字节数来看很小,但其实际消耗资源很大(例如调用其他合约等),因此要根据交易的具体操作收费,所有引入了汽油费这一概念。
在block header中包含了gaslimit,其并非将所有交易的消耗汽油费相加,而是该区块中所有交易能够消耗的资源的上限。

错误处理

以太坊中交易具有原子性,要么全执行,要么全不执行,不会只执行一部分(包含智能合约)。
需要注意的是,在执行过程中产生错误导致回滚,已经消耗掉的汽油费是不会退回的。从而有效防止了恶意节点对全节点进行恶意调用。
在这里插入图片描述

在这里插入图片描述

嵌套调用是否发生回滚,取决于调用方式。

挖矿与智能合约执行

假设全节点要打包一些交易到区块中,其中存在某些交易是对智能合约的调用。全节点应该先执行智能合约再挖矿,还是先挖矿获得记账权后执行智能合约?

  1. 观点1:先挖矿后执行智能合约。因为如果先执行智能合约,后挖矿,可能导致同一智能合约被不同节点执行多次,因此可能会导致一个转账操作被执行多次,即转账了好多次。

实际上,这个观点很明显是没有理解区块链系统。实际上,一个在区块链上的区块中的智能合约,在收到合约后本地执行,修改本地的MPT,这样其必然在系统中所有节点中都得到了执行,因为这样才能保证系统中所有节点从一个状态转入另一个状态,从而保证系统的一致性。
如果存在一个全节点没有执行该智能合约,那么该全节点的状态就和其他节点不一致,则该系统就没有保持状态一致。
不要用中心化的思想考虑

  1. 观点2:先挖矿后执行智能合约。因为执行智能合约要收取汽油费,如果多个人都执行,会收取很多份智能合约。

错误,实际上只用成功挖出区块的矿工才能获得汽油费

  1. 观点3:先执行智能合约后挖矿。

实际上,这才是正确的。因为挖矿涉及到三棵树的根hash值
在介绍时候,我们常说执行智能合约时,要先从发起调用的账户扣除可能花费的最大汽油费,待执行完成后,有剩余再退还。这样介绍会令人感觉有些迷糊,那么每个节点都会执行智能合约,是不是每个节点都会扣除一份汽油费呢?
当然不是,这里就需要了解汽油费的扣除机制。
汽油费是怎么扣除的?
首先,之前在以太坊数据结构中介绍了以太坊中“三棵树”——状态树、交易树、收据树。这三棵树都位于全节点中,是全节点在本地维护的数据结构,记录了每个账户的状态等数据,所以该节点收到调用时,是在本地对该账户的余额减掉即可。所以多个全节点每人扣一次,仅仅是每个全节点各自在本地扣一次。
也就是说,智能合约在执行过程中,修改的都是本地的数据结构,只有在该智能合约被发布到区块链上,所有节点才需要同步状态,各自在本地执行该智能合约。

反思:如果先挖矿后执行智能合约会如何?
没有三棵树的根节点,挖出来的是非法区块,系统的节点不认

一些问题

发布到区块链上交易都是成功执行的吗?
为了防止恶意节点故意发布大量非法交易影响系统运行,对于其发布的交易即使无法成功执行也需要收取汽油费。但如果交易不被发布到区块链上,是无法收取汽油费的。
智能合约支持多线程吗?
不支持,根本就没有支持多线程的语句。因为以太坊本质为一个交易驱动的状态机,面对同一组输入,必须转移到一个确定的状态。但对于多线程来说,同一组输入的输入顺序不同,最终的结果可能不一致。
此外,其他可能导致执行结果不确定的操作也不支持,例如:产生随机数。因此,以太坊中的随机数是伪随机数。

以太坊中的函数,可以获得目前区块的信息
在这里插入图片描述
在这里插入图片描述

Receipt数据结构

每个交易执行完成后会形成一个收据,下图便为收据的数据结构。其中status域就说明了该交易执行的状况。
在这里插入图片描述

以太坊地址类型

第一个,以wei为单位的地址类型的余额中,uint256并不是指其包含一个类型为uint256的参数,而是指该变量本身为uint256类型的变量。
下面的函数意义与我们认知有所不同,也与address.balance不同。
例如:
address.balance指的是address这个账户的余额
address.transfer(12345),并非address向外转账12345Wei,因为这样没有收款人的address。所以,该函数指的是当前合约向address地址中转入了12345Wei。后面的函数都是该语义,这里是需要注意的,因为其与我们认知存在差异。
(最下面三个函数为三种调用方式,忘记的话可以查看ETH智能合约篇1中的解释)
在这里插入图片描述
在以太坊中,转账有以下三种方法。
transfer在转账失败后会导致连锁性回滚,抛出异常;而send转账失败会返回false,不会导致连锁性回滚。call的方式本意是用于发动函数调用,但是也可以进行转账。
前两者在调用时,只发生2300wei的汽油费,这点汽油费很少,只能写一个log,而call的方式则是将自己还剩下的所有汽油费全部发送过去(合约调用合约时常用call,没用完的汽油费会退回)。例如A合约调用B合约,而A不知道B要调用哪些合约,为了防止汽油费不足导致交易失败,A将自己所有汽油费发给B来减少失败可能性。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值