主页 > imtoken网页版 > 以太坊智能合约的原理和使用

以太坊智能合约的原理和使用

imtoken网页版 2024-01-25 05:13:09

文章目录

1. 智能合约概述 1.1 什么是智能合约

智能合约本质上是运行在区块链上的一段代码,代码的逻辑定义了合约的内容。

智能合约账户保存了合约当前的运行状态,包括当前余额(balance)、交易次数(nonce)、合约代码(code)、存储(存储数据结构为MPT,执行数据为合同存储在这里)。

1.2 solidity语言

智能合约最常用的语言是 Solidity,它在语法上接近 JavaScript。 如下所示:

在这里插入图片描述

“pragma solidity ^0.4.21”声明了solidity的版本号,不同版本之间在语法上有一些差异;

Contract相当于“类(class)”,它定义了一些状态变量。 Solidity 是一种强类型编程语言。 大多数类型类似于常见的编程语言。 地址类型是Solidity特有的类型;

映射哈希表不支持遍历,所以需要单独创建一个数组用于遍历键值,如上图中的哈希表bids和数组bidders;

event(事件)用于记录日志,使用emit调用日志函数;

constructor 是一个构造函数,在合约创建时只调用一次;

接下来的三个成员函数是公开的,可以被外部账户或合约账户调用。

比特币智能合约_比特币合约交易员_比特币合约多空比

2.外部账户调用智能合约

如果将外部账户转入另一个外部账户,则几乎与比特币的转入相同; 如果是转账到某个合约账户,那么就是这个账户的合约调用,调用的函数和参数在“TX DATA”字段中描述,如下图所示:

在这里插入图片描述

其他字段说明如下:

“SENDER ADDRESS”是发起转账的地址;

“TO CONTRACT ADDRESS”为收款合约账户;

"VALUE"为转账金额(金额为0表示只调用函数,不转账);

“GAS USED”是交易消耗的gas量;

“GAS PRICE”是单位汽油的价格;

“GAS LIMIT”是发起人愿意花费的最大gas量。

3.一个合约调用另一个合约

一个合约可以调用另一个合约,但是合约账户不能主动调用另一个合约,必须由外部账户发起。

3.1 直接调用

比特币智能合约_比特币合约多空比_比特币合约交易员

给定另一个合约的地址,直接调用它。 如下图所示,callAFooDirectly的参数是一个合约地址,将地址转化为合约实例,即可调用合约的foo函数:

在这里插入图片描述

如果a.foo()执行过程中抛出错误,那么callAFooDirectly也会抛出错误,本次调用将回滚。 ua是执行a.foo("直接调用foo")的返回值。

此外,您可以调整提供的gas量或通过.gas()和.value()提供一些ETH。

3.2 使用地址类型的call()函数

通过地址类型调用函数调用智能合约。 下面的例子相当于A(addr).foo("call foo by func call"),如下图所示:

在这里插入图片描述

调用函数的第一个参数被编码为 4 个字节,表示要调用的函数的签名。 其他参数会扩展为32字节,表示要调用的函数的参数。

返回一个布尔值表示被调用的函数已经执行完毕(true)或者抛出EVM异常(false),无法获取函数的返回值。 发起调用的函数没有产生异常,继续执行。

与直接调用类似,您也可以通过 .gas() 和 .value() 调整提供的气体量或提供一些 ETH。

3.3 proxy调用delegatecall()函数

代理调用 delegatecall() 函数的方式与 call() 相同,只是不能使用 .value()。

此外,call() 将切换到被调用智能合约的上下文。 delegatecall() 只使用给定地址的代码,其他属性(存储、余额等)取自当前合约。 delegatecall 的目的是使用存储在另一个合约中的库代码。

比特币合约交易员_比特币智能合约_比特币合约多空比

3.4应付

如果合约账户收到外部转账(“VALUE”字段不为0),调用的函数必须标记为应付账款。 例如,如下图所示,bid 函数接收拍卖出价,ether 存储在合约中,可以防止恶意出价:

在这里插入图片描述

3.5 fallback()函数

如果转账交易的“TX DATA”字段中没有指定函数名,或者该字段中的函数名不存在,则默认调用fallback函数。 如果合约中没有定义fallback函数(合约中不一定存在fallback函数),那么fallback函数就会被调用fail。 函数定义如下所示:

function() public [应付款]{

...

}

4. 智能合约的创建和运行 4.1 创建和运行

智能合约的代码写好后,必须编译成字节码。 创建合约时,外部账户向0x0地址发起转账交易,转账金额(“VALUE”字段)为0,但需要支付gas费,合约的code放在data中场地。

智能合约运行在 EVM(以太坊虚拟机)上,EVM 的搜索空间为 256 位。 以太坊是一个交易驱动的状态机。 调用智能合约的交易在区块链上发布后,每个矿工将执行交易并确定性地从当前状态转移到下一个状态。

4.2 汽油费

智能合约是一个图灵完备的编程模型。 理论上可以证明,没有一种算法可以判断任何程序是否会暂停。

比特币智能合约_比特币合约交易员_比特币合约多空比

以太坊使用gas fee机制来防止死循环。 当合约中的指令被执行时,会收取gas费,由发起交易的人支付。 EVM中不同指令的gas消耗是不同的。 简单的指令很便宜,而复杂的指令或需要存储状态的指令读取起来很昂贵。 读取公开数据时,不需要支付gas费。 汽油费在txdata结构体中,代码如下:

在这里插入图片描述

AccountNonce为交易号,用于防止重放攻击,Price为汽油单价,GasLimit为愿意支付的最大汽油量,Recipient为收款人地址,Amount为转账金额,Payload为合约function和parameters,也就是前面章节提到的参数。 上面描述的 txDATA 字段。

在实际系统中,当节点收到智能合约调用时,首先根据GasLimit从发起账户中扣除gas费,增加自己的账户余额,然后根据实际执行结果计算消耗的gas费,多余的汽油费将被退还。 如果执行时发现gas费不足,会造成回滚比特币智能合约,但执行时扣除的gas费不予退还;

气体限制:

区块头结构中的GasUsed指的是所有交易所用gas的总和; GasLimit是指区块内所有交易消耗gas的上限,与txData结构中的GasLimit无关,用于限制区块内消耗的资源(比特币使用小于1M的区块来限制)。 每个矿工在打包区块时可以根据父区块的GasLimit调整±1/1024。

异常合约执行的gas费

合约执行过程中的任何异常都会被回滚,同时扣除相应的gas费,所以错误的交易也会被收录到区块中。 合约执行结果体现在交易回执中,即Receipt结构中的Status字段。

其他节点收到区块后,会丢弃自己的执行结果,同时验证区块中的交易,包括验证扣除gas费是否合法。 然后更新本地数据结构,继续挖掘。

4.3 错误处理

智能合约的执行是原子的。 一旦遇到异常(比如gas费不足),除特殊情况外,所有执行操作都会回滚,不会只执行一部分。 智能合约中没有自定义try-catch结构,无法捕获异常。

可以抛出错误的语句如下:

4.4 嵌套调用

比特币合约交易员_比特币合约多空比_比特币智能合约

嵌套调用是指一个合约调用另一个合约中的函数。 如果被调用合约执行过程中出现异常,部分调用方法会导致发起调用的合约一起回滚,而call()函数调用不会连续回滚,只会使当前调用失败并返回 false 值。

一个合约直接向一个合约账户转账,没有指定调用哪个函数,仍然会造成嵌套调用,此时调用fallback函数。

5. 智能合约可以获得的信息

智能合约不支持任何导致执行结果不确定的操作,例如多线程下的内存访问、真随机数生成等。

另外,智能合约无法获取与系统环境相关的信息,因为每个系统的环境不同,所以只能获取一些固定的信息。

5.1 区块信息

智能合约可以获得的区块信息如下:

5.2 合约调用信息

智能合约可获取的合约调用信息如下:

6. 合约地址类型 6.1 合约地址调用

所有智能合约都可以显式转换为地址类型。 当前智能合约获取另一个合约的地址比特币智能合约,可以通过该地址调用对应合约的成员变量和函数,如下图:

6.2 转移到合约

向合约地址转账的方式如下: