45. 时间锁
WTF Solidity极简入门: 45. 时间锁
我最近在重新学 Solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新 1-3 讲。
所有代码和教程开源在 github: github.com/AmazingAng/WTF-Solidity
这一讲,我们介绍时间锁和时间锁合约。代码由Compound的Timelock合约简化而来。
时间锁
时间锁(Timelock)是银行金库和其他高安全性容器中常见的锁定机制。它是一种计时器,旨在防止保险箱或保险库在预设时间之前被打开,即便开锁的人知道正确密码。
在区块链,时间锁被DeFi
和DAO
大量采用。它是一段代码,他可以将智能合约的某些功能锁定一段时间。它可以大大改善智能合约的安全性,举个例子,假如一个黑客黑了Uniswap
的多签,准备提走金库的钱,但金库合约加了2天锁定期的时间锁,那么黑客从创建提钱的交易,到实际把钱提走,需要2天的等待期。在这一段时间,项目方可以找应对办法,投资者可以提前抛售代币减少损失。
时间锁合约
下面,我们介绍一下时间锁Timelock
合约。它的逻辑并不复杂:
-
在创建
Timelock
合约时,项目方可以设定锁定期,并把合约的管理员设为自己。 -
时间锁主要有三个功能:
- 创建交易,并加入到时间锁队列。
- 在交易的锁定期满后,执行交易。
- 后悔了,取消时间锁队列中的某些交易。
-
项目方一般会把时间锁合约设为重要合约的管理员,例如金库合约,再通过时间锁操作他们。
-
时间锁合约的管理员一般为项目的多签钱包,保证去中心化。
事件
Timelock
合约中共有4
个事件。
QueueTransaction
:交易创建并进入时间锁队列的事件。ExecuteTransaction
:锁定期满后交易执行的事件。CancelTransaction
:交易取消事件。NewAdmin
:修改管理员地址的事件。
1 | // 事件 |
状态变量
Timelock
合约中共有4
个状态变量。
admin
:管理员地址。delay
:锁定期。GRACE_PERIOD
:交易过期时间。如果交易到了执行的时间点,但在GRACE_PERIOD
没有被执行,就会过期。queuedTransactions
:进入时间锁队列交易的标识符txHash
到bool
的映射,记录所有在时间锁队列中的交易。
1 | // 状态变量 |
修饰器
Timelock
合约中共有2
个modifier
。
onlyOwner()
:被修饰的函数只能被管理员执行。onlyTimelock()
:被修饰的函数只能被时间锁合约执行。
1 | // onlyOwner modifier |
函数
Timelock
合约中共有7
个函数。
-
构造函数:初始化交易锁定时间(秒)和管理员地址。
-
queueTransaction()
:创建交易并添加到时间锁队列中。参数比较复杂,因为要描述一个完整的交易:target
:目标合约地址value
:发送ETH数额signature
:调用的函数签名(function signature)data
:交易的call dataexecuteTime
:交易执行的区块链时间戳。
调用这个函数时,要保证交易预计执行时间
executeTime
大于当前区块链时间戳+锁定时间delay
。交易的唯一标识符为所有参数的哈希值,利用getTxHash()
函数计算。进入队列的交易会更新在queuedTransactions
变量中,并释放QueueTransaction
事件。 -
executeTransaction()
:执行交易。它的参数与queueTransaction()
相同。要求被执行的交易在时间锁队列中,达到交易的执行时间,且没有过期。执行交易时用到了solidity
的低级成员函数call
,在第22讲中有介绍。 -
cancelTransaction()
:取消交易。它的参数与queueTransaction()
相同。它要求被取消的交易在队列中,会更新queuedTransactions
并释放CancelTransaction
事件。 -
changeAdmin()
:修改管理员地址,只能被Timelock
合约调用。 -
getBlockTimestamp()
:获取当前区块链时间戳。 -
getTxHash()
:返回交易的标识符,为很多交易参数的hash
。
1 | /** |
Remix
演示
1. 部署Timelock
合约,锁定期设为120
秒。
2. 直接调用changeAdmin()
将报错。
3. 构造更改管理员的交易。
为了构造交易,我们要分别填入以下参数:
address target, uint256 value, string memory signature, bytes memory data, uint256 executeTime
target
:因为调用的是Timelock
自己的函数,填入合约地址。value
:不用转入ETH,这里填0
。signature
:changeAdmin()
的函数签名为:"changeAdmin(address)"
。data
:这里填要传入的参数,也就是新管理员的地址。但是要把地址填充为32字节的数据,以满足以太坊ABI编码标准。可以使用hashex网站进行参数的ABI编码。例子:1
2编码前地址:0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2
编码后地址:0x000000000000000000000000ab8483f64d9c6d1ecf9b849ae677dd3315835cb2executeTime
:先调用getBlockTimestamp()
得到当前区块链时间,再在它的基础上加个150秒填入。
4. 调用queueTransaction
,将交易放入时间锁队列。
5. 在锁定期内调用executeTransaction
,调用失败。
6. 在锁定期满调用executeTransaction
,交易成功。
7. 查看新的admin
地址。
总结
时间锁可以将智能合约的某些功能锁定一段时间,大大减少项目方rug pull
和黑客攻击的机会,增加去中心化应用的安全性。它被DeFi
和DAO
大量采用,其中包括Uniswap
和Compound
。你投资的项目有使用时间锁吗?