17-eth 智能合约

简介

eth 的智能合约是用 solidity 编写的,很像 Java Script。

外部账户没有代码只有 合约账户才有代码

一个拍卖案例

一个拍卖案例,一个类吧。

截屏2025-01-08 20.33.03

截屏2025-01-08 20.40.12

上面的 mapping 是一个 address 到 uint 的映射, 名叫 bids(其实是个 hash 表),但是solidity中的 hash表不支持遍历,如果想要遍历,需要用别的东西来记录。上面的例子中用的是 数组 bidders 来记录。

调用智能合约

外部账户没有代码,只有合约账户有代码!

外部账户调用合约函数

如果A B都是普通账户,A —> B 就是一个普通的转账交易;

但是如果A B是合约账户 ,A —> B 就是一个对B合约的调用。

截屏2025-01-08 21.06.35

sender address 是发起的地址,to contract address 是被调用的合约的地址,Tx DATA 是被调用的函数。

合约调用另一个合约中的函数

直接调用

截屏2025-01-08 21.13.04

A 合约中 有一个 事件作记录用的,在方法中 用 emit 调用这个事件作log。

B 合约中,方法的参数是一个地址,方法中传入地址构建了一个A的对象的实例a,然后调用a对象的方法foo。

eth 中规定,一次交易只能由外部账户发起,不能由合约账户主动发起。
按照上面的例子,只能由 一个外部账户对象调了 B 合约账户的方法callAFooDirectly,通过这个方法调用A合约账户中的 foo 方法,而不是是直接由B账户来直接发起。

addr的call调用

截屏2025-01-08 21.25.42

这种写法好在可以避免被调用的合约异常导致 调用者的合约也报异常。

代理调用

截屏2025-01-08 21.36.07

这样可以在当前的合约中执行,避免进入被调用的合约环境。

上例的分析

截屏2025-01-08 20.33.03

一个eth 方法要接受外部转账就要写 payable。

这个拍卖例子中,调用 bid() 方法时,出价人要把 出价的币 转到这个合约账户中,比如你叫100个eth 就要实质性的转100个eth 过去,锁定在合约内直到拍卖结束。

拍卖结束以后,没有竞拍到的人调用 withdraw 方法,把竞拍时锁定的币取回来,不需要接受外部转账所以不用写 payable。

截屏2025-01-08 21.53.03

以前面这个转账的例子看,value 转的是0 ,没有实质性的转入,所以转账的方法 也不需要 payable。

fallback函数

截屏2025-01-08 21.54.40

正常:**A调用B这个合约,需要在转账交易的data 域中说明,A调用的是B合约中的哪一个函数。 **

特例: A 转了一笔钱到B合约中,但是data 域内没有说明,要调用 fallback 函数,还要有payable标注。

智能合约的创建和运行

Screenshot 2025-02-03 at 5.54.02 PM

这个基本的逻辑是 写一个协议 送到 链上了,以后别人要参与也是调用链上这个只能合约。

运行都是 单线程,没有并发执行能力。

Screenshot 2025-02-03 at 5.59.18 PM

Screenshot 2025-02-03 at 6.09.07 PM

如果烧完了gas 还没完成就不退了,直接回滚,如果烧了 一半gas 出错了,就退回 一半的gas 剩下的 gas 不退了,然后回滚。

Screenshot 2025-02-03 at 6.12.20 PM

如果遇到嵌套调用时,如果是直接调用可能引发一连串的回滚,但是 是 call 这样的调用形式,就只会返回一个 true or false。

一个转账,没有指定调用的函数,也可能因为fallback 匿名方法导致了嵌套调用。

Screenshot 2025-02-03 at 6.17.51 PM

gaslimit 指的是这个区块中的 gas 上限,限制把太多的交易打包进这个区块中的一种措施。

btc 是写死在协议中的,但是 eth 中的 gaslimit 是有一个浮动范围的。

通过上面的 block header 中的内容,可以知道一定是先 执行了 智能合约 再 开始挖矿的,因为,挖矿要计算 block header 中的内容,但是 ,智能合约计算以后 才能 在本地中生成好 状态树、交易树、收据树的 root 值;

由此可知,只要没有挖到的矿工,浪费了算力电力,在本地执行了 智能合约之后,只能帮助挖到矿的人验证一下 看看是否正确,没有得到 gas 费和挖矿奖励的补偿,其实非常吃亏。

Screenshot 2025-02-03 at 9.00.12 PM

Screenshot 2025-02-03 at 9.06.14 PM

Screenshot 2025-02-03 at 9.10.27 PM

Screenshot 2025-02-03 at 9.14.06 PM

上面的意思是,在c 这个合约中 往addr 这个地址中 转了 12345 的eth,这个addr 指的是目标的addr 。

拍卖例子讲解

Screenshot 2025-02-03 at 9.19.51 PM

Screenshot 2025-02-03 at 9.22.59 PM

Screenshot 2025-02-03 at 9.23.13 PM

Screenshot 2025-02-03 at 9.40.05 PM

这个合约内写的就是 把 拍卖合约的地址addr 转为一个实例 sa,然后用这个实例 sa 调用bid方法参与拍卖,把钱用value方法 转进去。

这是一个 合约账户,如果需要 调用,还需要一个外部账户 先调用 hackV1 这个合约账户,再间接参与到拍卖中。

Screenshot 2025-02-03 at 9.47.52 PM

此时我们看见这个拍卖结束时候的函数,它使用了一个 转账函数,直接往 黑客的合约账户地址中转帐而没有调用方法,会默认调用了 fullback 方法,但是 黑客合约中 没有fullback 方法,会调用失败返回异常,transfer转帐方法会出现连锁式回滚。

但是需要注意:

这个报错回滚,发生在本地,并没有同步在整条区块链中,所有人 都拿不到钱!

改良

Screenshot 2025-02-03 at 10.05.06 PM

Screenshot 2025-02-03 at 10.13.30 PM

在黑客的 fallback 匿名函数中 又进行了一次 withdraw 操作,而withdraw 操作中要等 转帐完毕之后才会给bids 数组中对应的值 赋值为 0,构成了你调用我,我等你的情况,然后跳过 withdraw 中的 赋值 0 的操作,并且因为有一个 send 转帐会再次调用对应合约中的fallback方法,让黑客反反复复执行fallback中的转帐。

Screenshot 2025-02-03 at 10.31.47 PM

把赋值0的操作提前了,

sender 和 transfer 的转帐都只转一个 很小的 够写log 的gas 费用而已,不足以让接收方用这个gas 再 发起一次交易。

任何 你转帐或者调用某个合约的方法,都有可能被它反过来调用你当前的合约,或者修改你的状态。