智能合约-1

目录

一、智能合约的创建和运行

        1.智能合约的创建

        2.汽油费

        3.错误处理

        4.嵌套调用

二、思考

        1.GasLimit和GasUsed

        2.以太坊中的GasLimit跟比特币的区别

一、智能合约的创建和运行

1.智能合约的创建

        智能合约由一个外部账户发起一个转账交易,转给0x0这个地址,然后把这个要发布合约的代码放到data域里面。创建一个合约,要发起一个转账交易,给0这个地址转账,转账的金额都是0,实际上并非真的要转帐,而是想发布一个智能合约。发布的这个智能合约的代码放到数据域就行了,合约的代码要编译成bytecode,然后在EVM上运行。JVM,Java Virtual Machine,目的是增强可移植性。EVM类似这种设计思想,通过加一层虚拟机,对智能合约的运行提供一个一致性平台。EVM有时叫做Worldwide Computer(全世界的一个计算机),EVM的寻址空间非常大,为256位,unsigned int就是256位。

        比较比特币和以太坊,这两种区块链的编程模型,设计理念差别非常大。比特币设计理念是简单,脚本语言的功能很有限,如:不支持循环。以太坊要提供一个图灵完备的编程模型,Turing-complete Programming Model,即很多功能在比特币平台上实现起来很困难,甚至是根本实现不了。而对于以太坊平台,实现起来就很容易。这样也存在问题:出现死循环怎么办?当一个全节点收到一个对智能合约的调用,怎么知道这个调用执行起来会不会导致死循环?

        但是没有办法,这实际上是一个Halting Problem停机问题,停机问题是不可解的。这里的问题不是NPC的,NPC的问题可解,但没有多项式时间的解法,很多NPC问题有很自然的指数级的解法,比如说哈密尔顿回路问题,判断一个图有没有哈密尔顿回路,这个其实是很容易解的,想要一个解法是很容易的,如果不考虑复杂度的话,可以把所有的可能性枚举一遍,如说有n个顶点,n个顶点的排列是n!这么多个组合,把每种组合检查一下是不是构成一个合法的回路,就知道他有没有哈密尔顿回路,所以哈密尔顿回路问题是可解的,只不过解的复杂度是指数级的。而停机问题是根本就不可解的,从理论上可以证明不存在这样一个算法,能够对任意给定的输入程序判断出这个程序是否会停机。这时我们会把这个问题推给发起交易的那个账户,以太坊引入了汽油费机制,即发起对智能合约的调用时需要支付相应的汽油费。

2.汽油费

(1)数据结构

type txdata struct {
    AccountNonce uint64          `json:"nonce"    gencodec:"required"`
    Price       *big.Int         `json:"gasPrice" gencodec:"required"`
    GasLimit    uint64           `json:"gas"      gencodec:"required"`
    Recipient   *common.Address  `json:"to"       rlp:"nil"` //nil means contract creation
    Amount      *big.Int         `json:"value"    gencodec:"required"`
    Payload     []byte           `json:"input"    gencodec:"required"`
图1-1

        当一个全节点收到一个对智能合约的调用时,先按照调用过程中给出的GasLimit算出可能花掉的最大汽油费,一次性的把这个汽油费从这个发起调用的账户上扣掉,然后再根据实际执行的情况,算出实际花了多少钱,多退少补。其实不叫多退少补,如果汽油费不够的会引起回滚。不同的指令消耗的汽油费是不一样的,简单的指令(如加法减法)消耗的汽油费是很少的,复杂的指令消耗的汽油费就比较多。取哈希运算,一条指令就可以完成,但汽油费比较贵。除了计算量之外,需要存储状态的指令,消耗的汽油费也比较大。相比之下,如果你仅仅是为了读取公共数据,那些指令可以是免费的。

3.错误处理

        以太坊中的交易执行起来具有原子性。一个交易要么全部执行,要么完全不执行,不会只执行一部分。这个交易既包含普通的转账交易,也包含对智能合约的调用,所以如果在执行智能合约的过程当中,出现任何错误,会导致整个交易的执行回滚,退回到开始执行的之前的状态,就好像这个交易完全没有执行过那么什么情况下会出现错误呢?

(1)交易执行完之后,发现汽油费没有达到当初的GasLimit,那么多余的汽油费会被退回到这个账户里。一开始按照最大的GasLimit把汽油费扣掉,最后运行完,还有剩下的,就会被退回;若交易执行到一半,GasLimit已经用尽,该合约的执行要退回到开始执行之前的状态,这是一种错误处理,这个时候已经消耗掉的汽油费是不退的。执行的状态要回滚,但已经耗掉的汽油费不退回,否则会有恶意的节点发动delayous service attack,发布一个计算量很大的合约,然后不停的调这个合约,每次调的时候给的汽油费都不够,最后汽油费还会退回来,那么对这个节点来说没有什么损失,但矿工白白浪费了很多资源。

(2)assert语句和require语句,都是用来判断某种条件,如果条件不满足的话,就会导致抛出异常。assert语句一般来说是用于判断某种内部条件,require语句一般用于判断某种外部条件,如判断函数的输入是否符合要求。

function bid() public payable {
    //对于能接收以太币的函数,关键字payable是必须的。
    
    //拍卖尚未结束
    require(now <= auctionEnd);
}

        上面这个例子中,这个bid简单的函数,这里对该函数做判断,当前时间now<=拍卖的结束时间auctionEnd,则继续执行,否则,会抛出异常。第三个语句是revert(),无条件的抛出异常,如果执行到revert语句,那么会自动回滚。早期版本里用的是throw语句,新版本solidity里建议改用revert这个语句。solidity当中没有这种try-catch这种结构,有的编程语言像Java(用户自己可以定义出现问题后怎么办,他有这种try-catch)。

4.嵌套调用

        智能合约出现错误会导致回滚如果嵌套调用时(一个智能合约调用另外一个智能合约),被调用的这个智能合约出现错误,不一定会导致连锁式回滚(发起调用的智能合约跟着一起回滚),取决于调用这个智能合约的方式,如果是直接调用的话,会出现连锁式的回滚,整个交易都会回滚;如果调用方式是call这种,就不会引起连锁式回滚,只会使当前的调用失败返回一个false的返回值。有些情况下,从表面上看你并没有调用任何一个函数,比如就是往一个账户里转账,如果该账户是合约账户的话,转账操作本就有可能触发对函数的调用(因为有fallback()函数),这就是一种嵌套调用。一个合约往另一个合约里转账,就有可能调用这个合约里的fallback函数。

二、思考

1.GasLimit和GasUsed

        Block Header里面的GasLimit和GasUsed,与汽油费相关,Block Header里面的GasUsed是该区块里所有交易所消耗的汽油费加在一起。GasLimit其实不是这样的,发布区块需要消耗一定的资源,消耗的资源有一个限制。

        比特币当中对于发布的区块有一个限制的,大小的限制,最多不能超过1M,发布的区块如果没有任何限制,有的矿工可能把特别多的交易全部打包到一个区块里面然后发布出去,那么这个超大的区块在区块链上会消耗很多资源,所以它规定每个区块最多不能超过1M。比特币交易比较简单,基本上可以用交易的字节数来衡量出这个交易消耗的资源有多少,但以太坊中智能合约的逻辑很复杂,有的交易可能从字节数上看是很小的,但他消耗的资源可能很大,比如他可能调用别的合约之类的,所以要根据交易的具体操作来收费,这就是汽油费。

        GasLimit就是该区块里所有交易能够消耗汽油的上限,并不是把区块里每个交易的GasLimit加在一起,那样的话相当于没有限制了。每个交易的GasLimit是发布这个交易的账户自己定的,定多少是自己说了算,但是这个区块中的所有交易,实际能够消耗的汽油是有上限的,否则可能发布一个对资源消耗很大的一个区块出去,对整个系统的运行是没有好处的。

2.以太坊中的GasLimit跟比特币的区别

        比特币限制资源是按照大小来限制的(上限1M),是写在协议里的。有些人认为1M太小了,而且有的分叉币的产生就是为了提高这个上限。以太坊中也有一个上限——GasLimit,但每个矿工在发布区块的时候都可以对GasLimit微调,可以在GasLimit的基础上上调或者下调\frac{1}{1024}。若出现像比特币那种情况,大家都觉得这个GasLimit太小了,那发布区块的时候可以增加1/1024。由于以太坊的出块速度很快,平均出块时间只有十几秒,如果大家都觉得当前的GasLimit太小,很快就可以翻一番。当然,相同的道理也可能下调,所以这种机制实际上求出的GasLimit,是所有矿工认为比较合理的GasLimit的一个平均值,有的矿工认为要上调,有的矿工认为要下调,那么每个矿工在获得记账权之后就按照自己的意愿进行这种上调或者下调的微调,最后整个系统的GasLimit就趋向于所有矿工的一个平均意见。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐