【从0学习Solidity】45. 时间锁

【从0学习Solidity】45. 时间锁

在这里插入图片描述

  • 博主简介:不写代码没饭吃,一名全栈领域的创作者,专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构,分享一些项目实战经验以及前沿技术的见解。
  • 关注我们的主页,探索全栈开发,期待与您一起在移动开发的世界中,不断进步和创造!
  • 本文收录于 不写代码没饭吃 的学习汇报系列,大家有兴趣的可以看一看。
  • 欢迎访问我们的微信公众号:不写代码没饭吃,获取更多精彩内容、实用技巧、行业资讯等。您关注的是我们前进的动力!

这一讲,我们介绍时间锁和时间锁合约。代码由Compound的Timelock合约简化而来。

时间锁

45-1.jpeg

时间锁(Timelock)是银行金库和其他高安全性容器中常见的锁定机制。它是一种计时器,旨在防止保险箱或保险库在预设时间之前被打开,即便开锁的人知道正确密码。

在区块链,时间锁被DeFiDAO大量采用。它是一段代码,他可以将智能合约的某些功能锁定一段时间。它可以大大改善智能合约的安全性,举个例子,假如一个黑客黑了Uniswap的多签,准备提走金库的钱,但金库合约加了2天锁定期的时间锁,那么黑客从创建提钱的交易,到实际把钱提走,需要2天的等待期。在这一段时间,项目方可以找应对办法,投资者可以提前抛售代币减少损失。

时间锁合约

下面,我们介绍一下时间锁Timelock合约。它的逻辑并不复杂:

  • 在创建Timelock合约时,项目方可以设定锁定期,并把合约的管理员设为自己。

  • 时间锁主要有三个功能:

    • 创建交易,并加入到时间锁队列。
    • 在交易的锁定期满后,执行交易。
    • 后悔了,取消时间锁队列中的某些交易。
  • 项目方一般会把时间锁合约设为重要合约的管理员,例如金库合约,再通过时间锁操作他们。

  • 时间锁合约的管理员一般为项目的多签钱包,保证去中心化。

事件

Timelock合约中共有4个事件。

  • QueueTransaction:交易创建并进入时间锁队列的事件。
  • ExecuteTransaction:锁定期满后交易执行的事件。
  • CancelTransaction:交易取消事件。
  • NewAdmin:修改管理员地址的事件。
    // 事件// 交易取消事件event CancelTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature,  bytes data, uint executeTime);// 交易执行事件event ExecuteTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature,  bytes data, uint executeTime);// 交易创建并进入队列 事件event QueueTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint executeTime);// 修改管理员地址的事件event NewAdmin(address indexed newAdmin);

状态变量

Timelock合约中共有4个状态变量。

  • admin:管理员地址。
  • delay:锁定期。
  • GRACE_PERIOD:交易过期时间。如果交易到了执行的时间点,但在GRACE_PERIOD没有被执行,就会过期。
  • queuedTransactions:进入时间锁队列交易的标识符txHashbool的映射,记录所有在时间锁队列中的交易。
    // 状态变量address public admin; // 管理员地址uint public constant GRACE_PERIOD = 7 days; // 交易有效期,过期的交易作废uint public delay; // 交易锁定时间 (秒)mapping (bytes32 => bool) public queuedTransactions; // txHash到bool,记录所有在时间锁队列中的交易

修饰器

Timelock合约中共有2modifier

  • onlyOwner():被修饰的函数只能被管理员执行。
  • onlyTimelock():被修饰的函数只能被时间锁合约执行。
    // onlyOwner modifiermodifier onlyOwner() {require(msg.sender == admin, "Timelock: Caller not admin");_;}// onlyTimelock modifiermodifier onlyTimelock() {require(msg.sender == address(this), "Timelock: Caller not Timelock");_;}

函数

Timelock合约中共有7个函数。

  • 构造函数:初始化交易锁定时间(秒)和管理员地址。

  • queueTransaction():创建交易并添加到时间锁队列中。参数比较复杂,因为要描述一个完整的交易:

    • target:目标合约地址
    • value:发送ETH数额
    • signature:调用的函数签名(function signature)
    • data:交易的call data
    • executeTime:交易执行的区块链时间戳。

    调用这个函数时,要保证交易预计执行时间executeTime大于当前区块链时间戳+锁定时间delay。交易的唯一标识符为所有参数的哈希值,利用getTxHash()函数计算。进入队列的交易会更新在queuedTransactions变量中,并释放QueueTransaction事件。

  • executeTransaction():执行交易。它的参数与queueTransaction()相同。要求被执行的交易在时间锁队列中,达到交易的执行时间,且没有过期。执行交易时用到了solidity的低级成员函数call,在第22讲中有介绍。

  • cancelTransaction():取消交易。它的参数与queueTransaction()相同。它要求被取消的交易在队列中,会更新queuedTransactions并释放CancelTransaction事件。

  • changeAdmin():修改管理员地址,只能被Timelock合约调用。

  • getBlockTimestamp():获取当前区块链时间戳。

  • getTxHash():返回交易的标识符,为很多交易参数的hash

    /*** @dev 构造函数,初始化交易锁定时间 (秒)和管理员地址*/constructor(uint delay_) {delay = delay_;admin = msg.sender;}/*** @dev 改变管理员地址,调用者必须是Timelock合约。*/function changeAdmin(address newAdmin) public onlyTimelock {admin = newAdmin;emit NewAdmin(newAdmin);}/*** @dev 创建交易并添加到时间锁队列中。* @param target: 目标合约地址* @param value: 发送eth数额* @param signature: 要调用的函数签名(function signature)* @param data: call data,里面是一些参数* @param executeTime: 交易执行的区块链时间戳** 要求:executeTime 大于 当前区块链时间戳+delay*/function queueTransaction(address target, uint256 value, string memory signature, bytes memory data, uint256 executeTime) public onlyOwner returns (bytes32) {// 检查:交易执行时间满足锁定时间require(executeTime >= getBlockTimestamp() + delay, "Timelock::queueTransaction: Estimated execution block must satisfy delay.");// 计算交易的唯一识别符:一堆东西的hashbytes32 txHash = getTxHash(target, value, signature, data, executeTime);// 将交易添加到队列queuedTransactions[txHash] = true;emit QueueTransaction(txHash, target, value, signature, data, executeTime);return txHash;}/*** @dev 取消特定交易。** 要求:交易在时间锁队列中*/function cancelTransaction(address target, uint256 value, string memory signature, bytes memory data, uint256 executeTime) public onlyOwner{// 计算交易的唯一识别符:一堆东西的hashbytes32 txHash = getTxHash(target, value, signature, data, executeTime);// 检查:交易在时间锁队列中require(queuedTransactions[txHash], "Timelock::cancelTransaction: Transaction hasn't been queued.");// 将交易移出队列queuedTransactions[txHash] = false;emit CancelTransaction(txHash, target, value, signature, data, executeTime);}/*** @dev 执行特定交易。** 要求:* 1. 交易在时间锁队列中* 2. 达到交易的执行时间* 3. 交易没过期*/function executeTransaction(address target, uint256 value, string memory signature, bytes memory data, uint256 executeTime) public payable onlyOwner returns (bytes memory) {bytes32 txHash = getTxHash(target, value, signature, data, executeTime);// 检查:交易是否在时间锁队列中require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");// 检查:达到交易的执行时间require(getBlockTimestamp() >= executeTime, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");// 检查:交易没过期require(getBlockTimestamp() <= executeTime + GRACE_PERIOD, "Timelock::executeTransaction: Transaction is stale.");// 将交易移出队列queuedTransactions[txHash] = false;// 获取call databytes memory callData;if (bytes(signature).length == 0) {callData = data;} else {callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);}// 利用call执行交易(bool success, bytes memory returnData) = target.call{value: value}(callData);require(success, "Timelock::executeTransaction: Transaction execution reverted.");emit ExecuteTransaction(txHash, target, value, signature, data, executeTime);return returnData;}/*** @dev 获取当前区块链时间戳*/function getBlockTimestamp() public view returns (uint) {return block.timestamp;}/*** @dev 将一堆东西拼成交易的标识符*/function getTxHash(address target,uint value,string memory signature,bytes memory data,uint executeTime) public pure returns (bytes32) {return keccak256(abi.encode(target, value, signature, data, executeTime));}

Remix演示

1. 部署Timelock合约,锁定期设为120秒。

45-1.jpg

2. 直接调用changeAdmin()将报错。

45-2.jpg

3. 构造更改管理员的交易。

为了构造交易,我们要分别填入以下参数:
address target, uint256 value, string memory signature, bytes memory data, uint256 executeTime

  • target:因为调用的是Timelock自己的函数,填入合约地址。
  • value:不用转入ETH,这里填0
  • signaturechangeAdmin()的函数签名为:"changeAdmin(address)"
  • data:这里填要传入的参数,也就是新管理员的地址。但是要把地址填充为32字节的数据,以满足以太坊ABI编码标准。可以使用hashex网站进行参数的ABI编码。例子:
    编码前地址:0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2
    编码后地址:0x000000000000000000000000ab8483f64d9c6d1ecf9b849ae677dd3315835cb2
    
  • executeTime:先调用getBlockTimestamp()得到当前区块链时间,再在它的基础上加个150秒填入。
    45-3.jpg

4. 调用queueTransaction,将交易放入时间锁队列。

45-4.jpg

5. 在锁定期内调用executeTransaction,调用失败。

45-5.jpg

6. 在锁定期满调用executeTransaction,交易成功。

45-6.jpg

7. 查看新的admin地址。

45-7.jpg

总结

时间锁可以将智能合约的某些功能锁定一段时间,大大减少项目方rug pull和黑客攻击的机会,增加去中心化应用的安全性。它被DeFiDAO大量采用,其中包括UniswapCompound。你投资的项目有使用时间锁吗?

在这里插入图片描述

如果这份博客对大家有帮助,希望各位给作者一个免费的点赞👍作为鼓励,并评论收藏一下⭐,谢谢大家!!!
制作不易,如果大家有什么疑问或给作者的意见,欢迎评论区留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/142019.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

关于vantUI的导航组件tab标签页在ios和安卓中运用遇到的坑

vantTab的默认值 应用场景问题描述原始代码更正代码 应用场景 根据路由传值设置默认tab页&#xff0c;获取不同的数据并进行展示 问题描述 ios可正常按照路由传值默认tab页&#xff0c;安卓始终默认tabList的第一个value值&#xff0c;疑安卓系统中不接受dataMap.tabActive为…

YOLOv5如何训练自己的数据集(生活垃圾数据集为例)

文章目录 前言1、数据标注说明2、定义自己模型文件3、训练模型参考文献 前言 本文主要介绍如何利用YOLOv5训练自己的数据集 1、数据标注说明 以生活垃圾数据集为例子 生活垃圾数据集&#xff08;YOLO版&#xff09;点击这里直接下载本文生活垃圾数据集 生活垃圾数据集组成&…

Python开发与应用实验2 | Python基础语法应用

*本文是博主对学校专业课Python各种实验的再整理与详解&#xff0c;除了代码部分和解析部分&#xff0c;一些题目还增加了拓展部分&#xff08;⭐&#xff09;。拓展部分不是实验报告中原有的内容&#xff0c;而是博主本人自己的补充&#xff0c;以方便大家额外学习、参考。 &a…

【JavaEE】多线程案例-线程池

文章目录 1. 什么是线程池2. 为什么要使用线程池&#xff08;线程池有什么优点&#xff09;3. 如何使用Java标准库提供的线程池3.1 创建一个线程池对象3.2 什么是工厂模式3.3 为什么要使用工厂模式3.4 Executors 创建不同具有不同特性的线程池3.5 ThreadPool 类的构造方法3.6 线…

C++之list成员函数应用总结(二百三十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

Neo4j 与 Cypher 基础

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 简介 Neo4j 是用 Java 实现的开源 NoSQL 图数据库。从2003年开始开发&#xff0c;2007年正式发布第一版&#xff0c;其源码托管于 GitHub。 与常见的关系型数据库不同&#xff0c;Neo4j 基于图图结构来表示…

学习:原码-反码-补码

文章目录 前提知识原码详解反码补码 二进制负数的运算 前提知识 正数不需要进行原码反码补码一说&#xff0c;正数就是正数&#xff0c;我们原码反码补码是为了针对负数 &#xff08;按道理来说其实根本不存在什么码&#xff0c;只有二进制机器码&#xff0c;不过是为了方便计…

华为云云耀云服务器L实例评测|华为云上安装etcd

文章目录 华为云云耀云服务器L实例评测&#xff5c;华为云上安装etcd一、什么是etcd官方硬件建议 二、华为云主机准备三、etcd安装1. 安装预构建的二进制文件2. 从源代码构建 四、etcd服务注册与发现1. 配置etcd2. 使用systemctl 管理启动etcd服务3. 注册服务4. 发现服务 五、其…

ISE_ChipScope Pro的使用

1.ChipScope Pro Core Inserter 使用流程 在之前以及编译好的流水灯实验上进行学习 ChipScope的使用。 一、新建一个ChipScope 核 点击Next,然后在下一个框中选择 Finish&#xff0c;你就会在项目菜单中看到有XX.cdc核文件。 二、对核文件进行设置 右键“Synthesize – XST” …

最快的包管理器--pnpm创建vue项目完整步骤

1.用npm全局安装pnpm npm install -g pnpm 2.在要创建vue项目的包下进入cmd&#xff0c;输入&#xff1a; pnpm create vue 3.输入项目名字&#xff0c;选择Router,Pinia,ESLint,Prettier之后点确定 4.cd到创建好的项目 &#xff0c;安装依赖 cd .\刚创建好的项目名称\ p…

怎样快速打开github.com

1访问这个网站很慢是因为有DNS污染&#xff0c;被一些别有用心的人搞了鬼了&#xff0c; 2还有一个重要原因是不同的DNS服务器解析的速度不一样。 1 建议设置dns地址为114.114.114.114.我觉得假设一个县城如果有一个DNS服务器的话&#xff0c;这个服务器很可能不会存储…

[linux]服务器挂代理提升下载权重速度

写在前面 这里主要以huggingface下载权重为例&#xff0c;介绍如何在linux中部署代理提升下载速度 实际操作 第一步&#xff1a;服务器安装clash文件 https://github.com/Dreamacro/clash/releases#下载clash链接第二步&#xff1a;使用自己的配置文件 将config.yaml替换掉…

前端项目练习(练习-003-webpack-01)

学习webpack前&#xff0c;首先&#xff0c;创建一个web-003项目&#xff0c;内容和web-002一样。&#xff08;注意将package.json中的name改为web-003&#xff09; 想想&#xff0c;我们开发Java 的时候&#xff0c;Maven帮我们做的主要是编译&#xff0c;打包等等内容。开发前…

Spring Cloud Alibaba Gateway 简单使用

文章目录 Spring Cloud Alibaba Gateway1.Gateway简介2. 流量网关和服务网关的区别3. Spring Cloud Gateway 网关的搭建3.1 Spring Cloud Gateway 配置项的说明3.2 依赖导入3.3 配置文件 Spring Cloud Alibaba Gateway 1.Gateway简介 Spring Cloud Gateway是一个基于Spring F…

计算机竞赛 深度学习乳腺癌分类

文章目录 1 前言2 前言3 数据集3.1 良性样本3.2 病变样本 4 开发环境5 代码实现5.1 实现流程5.2 部分代码实现5.2.1 导入库5.2.2 图像加载5.2.3 标记5.2.4 分组5.2.5 构建模型训练 6 分析指标6.1 精度&#xff0c;召回率和F1度量6.2 混淆矩阵 7 结果和结论8 最后 1 前言 &…

构建自己的物料解决方案——构建物料库,实现前端设计

01: 数据拦截简化数据获取流程 /** * 响应拦截器&#xff1a; * 服务端返回数据之后&#xff0c;前端 .then 之前被调用 */ service.interceptors.response.use((response) > {const { success, message, data } response.dataif (success) {return data}// TODO&#xff…

法规标准-UN R48标准解读

UN R48是做什么的&#xff1f; UN R48全名为关于安装照明和灯光标志装置的车辆认证的统一规定&#xff0c;主要描述了对各类灯具的布置要求及性能要求&#xff1b;其中涉及自动驾驶功能的仅有6.25章节【后方碰撞预警信号】&#xff0c;因此本文仅对此章节进行解读 功能要求 …

Python中的设计模式 -- 单例

迷途小书童 读完需要 2分钟 速读仅需 1 分钟 当我们谈到单例模式时&#xff0c;可以想象一个非常特殊的餐厅&#xff0c;这个餐厅只有一个桌子&#xff0c;无论多少人来用餐&#xff0c;都只能坐在这个桌子上。这个桌子就是餐厅的单例&#xff0c;它保证了整个餐厅中只有一个桌…

Element登录+注册

Element登录注册 1.1 定义1.3 完成用户注册登录界面搭建1.3.3 下载js依赖1.3.4 创建用户登录注册组件1.3.5 配置路由 二、数据交互2.1 数据导入2.3 安装引用相关模块 2.3.1 安装相关模块2.3.2 引用相关模块2.4 axios之get请求2.5 axios之post请求 四、注册 1.1 定义 ElementUI是…

bash中执行比较的几种方法

bash 脚本中的 test 命令用于检查表达式的有效性&#xff0c;检查命令或表达式为 true 或者 false。此外&#xff0c;它还可以用于检查文件的类型和权限。 如果命令或表达式有效&#xff0c;则 test 命令返回0&#xff0c;否则返回1。 使用 test 命令 test 命令的基本语法如…