世界杯竞猜项目Dapp-第五章(合约升级)

news2024/11/25 13:32:44

目前主流有三种合约升级方法

  • transparent 方式;(通用,业务逻辑和代理逻辑解耦合,比较贵)
  • uups 方式;(代理逻辑集成到了业务逻辑,通过继承来实现,便宜)
  • beacon 方式;(更加高级,一个信号,升级多个合约)

本次采用 transparent 方式,具体实现思路即,引入一个代理合约 Proxy(蓝色),用户仅与这个代理合约进行交互,由代理合约去与业务合约进行交互,因此在业务合约发生变化(升级)的时候,用户无感,并且历史数据也能够保留下来,如下图所示:
在这里插入图片描述
既然业务合约可以随意切换,那用户数据就只能存储在代理合约中了,在实际进行业务处理时,数据读写都是从代理合约来的,即数据与逻辑分离,其实现的核心便是 delegatecall 关键字。在此之前,先对 solidity 提供的三个合约调用方法:call、staticdall、delegatecall 进行对比说明
在这里插入图片描述

  • call:主要是进行常规的合约调用,比如进行合约向普通EOA进行转账时,语法为:to.call{value: value}(“”),此时目标合约中的msg.sender是调用者,即Caller Contract;
  • staticcall:与call类似,但是它不会修改合约状态,单纯调用计算而已,原理同上;(不常用)
  • delegatecall:这个是专门为代理合约 Proxy 准备的,作用是帮助用户 User 来调用 Target Contract 合约

delegatecall 特点:
1、从 Target Contract 角度来看:msg.sender 是 user,而不是 Proxy,即 Proxy 对 user 的请求进行了透传;
2、在 Target Contract 被调用时,使用的是 Proxy 的上下文,即执行合约带来的状态变化会存在 Proxy 中,而不是 Target Contract 之中
(注:由于 Transparent 模式升级时,implementation 和 proxy 不用相互关心彼此的 storage 数据,因此这种模式被称为:unstructed storage)

存储冲突问题

在 solidity 中,状态变量存储在 slot 中,slot 可以理解为 key-value 存储空间,evm 为每个合约代码提供了最多 2^256个 slot,每个 slot 可以最多存储 32 字节数据。状态变量一般是从 slot 0 开始进行存储的,在使用 delegatecall 的时候,由于需要在 Proxy 的 slot 中存储目标合约中指定的数据结构,此时如果 proxy 的 storage 布局与目标合约的 storage 布局不相同,那么就会出现存储冲突(Storage collisioin)的问题。
在这里插入图片描述
slot 0 分别存储的是逻辑合约地址 _imp 和管理员地址 _owner,出现存储冲突。

解决存储冲突问题

在代理合约 Proxy 中,一共需要指定两个状态变量:

  • 逻辑合约地址 implementation,用来指明被代理的合约;
  • Admin,代理合约的管理员,有权限进行合约升级;

因此,如果不进行特殊处理,则一定会出现存储 slot 冲突,我们可以将 Proxy 中的默认 slot 留出来,不要占用,而是在代理合约使用指定的 slot 来存储逻辑合约 _imp 和 admin 地址,这个解决方案也叫 EIP-1967

# implementation slot 生成规则:
bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1), 

# admin slot 生成规则:
bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)),

升级规则

1 在升级的合约中,如果有新变量的添加,那么新的状态变量只能在原始合约状态末尾依次往后添加,否则也会导致状态变量布局不一致,出现存储冲突;
2 如果一个合约定义为可升级的,那么这个合约不能有构造函数,需要使用initialize 函数来代替初始化工作。因为我们需要将部署时的数据存储在 Proxy 合约中,如果提供了构造函数,这些数据就会错误地写在逻辑合约中。

WorldCup 升级改造

首先在 node_modules 目录下安装合约升级地标准库,以使用初始化函数

npm i @openzeppelin/contracts-upgradeable

创建 WorldCupV1.sol,在原有 WorldCup 基础上修改代码

    //1. 导入标准包
    import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

    //2. 继承
    contract WorldCupV1 is Initializable {  
      //3. 将构造函数替换为初始化函数 constructor(uint256 _deadline) 
      function initialize(uint256 _deadline) public initializer {
          admin = msg.sender;
          require(
              _deadline > block.timestamp,
              "WorldCupLottery: invalid deadline!"
          );
          deadline = _deadline;
      }   
    }

再创建升级后的合约 WorldCupV2.sol,修改如下:

event ChangeDeadline(uint256 _prev, uint256 _curr);
uint256 changeCount

// 1. 增加函数,支持修改deadline
function changeDeadline(uint256 _newDeadline) external {
    require(_newDeadline > block.timestamp, "invalid timestamp!");

    // 2.增加新事件
    emit ChangeDeadline(deadline, _newDeadline); 

    // 4.状态变量
    changeCount++;  
    deadline = _newDeadline;
}

安装升级插件,在配置文件中导入

$ npm install --save-dev @openzeppelin/hardhat-upgrades

// hardhat.config.js
require('@openzeppelin/hardhat-upgrades');

编写升级脚本,创建 scripts/deployAndUpgrade.ts:

const { ethers, upgrades } = require("hardhat");

async function main() {
  const TWO_WEEKS_IN_SECS = 14 * 24 * 60 * 60;
  const timestamp = Math.floor(Date.now() / 1000)
  const deadline = timestamp + TWO_WEEKS_IN_SECS;
  console.log('deadline:', deadline)

  // Deploying
  const WorldCupv1 = await ethers.getContractFactory("WorldCupV1");
  const instance = await upgrades.deployProxy(WorldCupv1, [deadline]);
  await instance.deployed();
  console.log("WorldCupV1 address:", instance.address);
  console.log("deadline1:", await instance.deadline())

  console.log('ready to upgrade to V2...');

  // Upgrading
  const WorldCupV2 = await ethers.getContractFactory("WorldCupV2");
  const upgraded = await upgrades.upgradeProxy(instance.address, WorldCupV2);
  console.log("WorldCupV2 address:", upgraded.address);

  await upgraded.changeDeadline(deadline + 100)
  console.log("deadline2:", await upgraded.deadline())
}

main();

部署升级合约

npx hardhat run scripts/upgrade/deployAndUpgrade.ts --network goerli

在这里插入图片描述
我们还没 verify 业务合约 WorldCupV1 和 WorldCupV2,然后与当前的代理合约 Proxy 关联起来,我们通过 internal Txns 可以找到 WorldCupV2 的合约地址:
在这里插入图片描述
都对其进行 verify,并查看数据,发现都是空的
在这里插入图片描述

关联合约

找到代理合约-> More Options -> Is this a proxy? -> Verify -> Save
在这里插入图片描述
然后再回到当前页面刷新后,页面上多了两个按钮:Read as Proxy 和 Write as Proxy 如图:
在这里插入图片描述
我们最终暴露给用户的地址就是这个代理合约,用户的所有操作都相当于在读写着两个新方法,这两个方法会被 Proxy 传递到逻辑合约中,并把执行结果返回到代理合约中

TransparentProxy 合约关系

通过hardhat-upgrade包执行部署后,一共会自动部署三个合约:

  • 代理合约:TransparentUpgradebleProxy
  • 代理合约的管理员合约:proxyAdmin
  • 业务合约:implementation
    在这里插入图片描述

当我们使用脚本执行合约升级的时候,此时内部交互为:

  • 自动部署 WorldCupV2 合约
  • 调用 ProxyAdmin 的 upgrade 方法,进行升级

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

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

相关文章

408 考研《操作系统》第三章第二节:基本分页存储管理、两级页表、基本分段存储管理方式、段页式管理方式

文章目录教程1. 基本分页存储管理的基本概念1.1 连续分配方式的缺点1.2 把“固定分区分配”改造为“非连续分配版本”1.3 什么是分页存储1.4 如何实现地址的转换?1.5 逻辑地址结构1.6 重要的数据结构——页表1.7 知识回顾与重要考点2. 基本地址变换机构2.1 例题2.2 …

node.js+uni计算机毕设项目购物小程序(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置: Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术: Express框架 Node.js Vue 等等组成,B/S模式 Vscode管理前后端分离等…

不写一行代码(一):实现安卓基于GPIO的LED设备驱动

文章目录系列文章一、前言二、准备工作2.1 内核版本2.2 内核文档:bindings->leds2.3 文档解析: leds-gpio.txt三、编写DTS3.1 查原理图,挑选GPIO3.2 编写DTS文件四、编译测试4.1 编译dt.img4.2 烧录dt.img五、基于fs的测试5.1 测试命令5.2…

第二十五章 数论——约数

第二十五章 数论——约数一、什么是约数二、约数的求解——试除法1、问题2、思路分析3、代码实现三、约数个数1、问题描述2、算法思路3、代码实现四、约数之和1、问题描述2、算法思路3、代码实现五、最大公约数——欧几里德算法1、问题描述2、算法思路(1&#xff09…

前端实现文件上传(点击+拖拽)

一、简介 之前在Vue项目中使用过element的上传组件,实现了点击上传拖拽上传的两种上传功能。然后我就在想是否可以通过原生的htmljs来实现文件的点击上传和拖拽上传,说干就干。 首先是点击获取上传文件自然没的说,只需要借助input标签即可&a…

Java面试题(六)RabbitMQ常见面试题

RabbitMQ常见面试题RabbitMQ架构设计RabbitMQ有哪些优点RabbitMQ事物机制RabbitMQ持久化机制持久化和非持久化什么时候需要持久化?落盘过程文件删除RabbitMQ如何保证消息不丢失RabbitMQ如何保证消息不被重复消费RabbitMQ死信队列,延时队列死信队列延时队…

如何使用 ChatGPT 进行教学,教师可以利用 ChatGPT 的 5 种方式

我们听说过很多关于学生如何使用 ChatGPT 撰写论文和布置家庭作业的信息。 我们一直在讨论围绕这个问题的担忧,并争先恐后地为 ChatGPT 寻找 AI 检测工具,据传 OpenAI 也在致力于此。 但是关于教师如何将 ChatGPT 用于他们自己的工作的讨论并不多。 在从教师的角度对 Chat…

20221222 Coppeliasim的视频导出功能

Video exporter CoppeliaSim的视频记录器可以对the page, scene hierarchy and model browser areas进行录屏,保存成视频文件。Dialogs, menu bar, toolbars and popup menus (对话框、菜单栏、工具栏和弹出菜单)不会被录进去. 默认情况下&am…

【C++11】包装器

目录 1.function包装器 1.1什么是函数包装器(function)&#xff1f; 1.2为啥使用函数包装器(function)&#xff1f; 2.bind包装器 2.1绑定普通函数和调整传参顺序 2.2绑定类成员函数 1.function包装器 头文件#include<functional> 1.1什么是函数包装器(function)&a…

2022年终总结

不知不觉就到了年末&#xff0c;感叹时间过的真快。 我自己坚持写了七年多的博客&#xff0c;但这其实是我第一次去写年终总结。也不知道怎么写&#xff0c;就简单聊聊。 写博客的初衷就是个人收获&#xff0c;学习的记录&#xff0c;分享出来如果能帮到别人那就更好了。毕竟…

(一)LTspice简介2

文章目录前言一、LTspice的仿真过程二、spice的模型三、LTspice的工具栏和快捷键四、LTspice中的数量级前言 上一节我们学习了LTspice的安装&#xff0c;很简单&#xff0c;无脑安装 &#xff08;一&#xff09;LTspice安装 这一节我们继续学习LTspice的简介&#xff0c;主要包…

题:A-B 数对(二分)

A-B 数对 - 洛谷 题目背景 出题是一件痛苦的事情&#xff01; 相同的题目看多了也会有审美疲劳&#xff0c;于是我舍弃了大家所熟悉的 AB Problem&#xff0c;改用 A-B 了哈哈&#xff01; 题目描述 给出一串正整数数列以及一个正整数 CC&#xff0c;要求计算出所有满足 A…

通过Wireshark分析Apache服务器的SSL协议工作过程

文章目录一、实验环境二、为Apache服务器启用SSL1.获取SSL证书2.修改httpd.conf配置文件3.修改httpd-ssl.conf配置文件4.启动Apache服务三、SSL/TLS工作过程分析一、实验环境 操作系统&#xff1a;macOS Ventura 13.0.1 Apache&#xff1a;Apache/2.4.54 (Unix)&#xff0c;此…

喜提JDK的BUG一枚、多线程的情况下请谨慎使用这个类的stream遍历。

前段时间在 RocketMQ 的 ISSUE 里面冲浪的时候&#xff0c;看到一个 pr&#xff0c;虽说是在 RocketMQ 的地盘上发现的&#xff0c;但是这个玩意吧&#xff0c;其实和 RocketMQ 没有任何关系。 纯纯的就是 JDK 的一个 BUG。 我先问你一个问题&#xff1a;LinkedBlockingQueue…

vue3 antd table表格的样式修改(二)利用rowClassName给table添加行样式

vue3 antd项目实战——修改ant design vue组件中table表格的默认样式&#xff08;二&#xff09;知识调用场景复现修改table表格的行样式一、rowClassName添加行样式二、表格的不可控操作写在最后知识调用 文章中可能会用到的知识链接vue3ant design vuets实战【ant-design-vu…

Autosar MCAL-SPI配置及使用

文章目录前言SPI协议基础Autosar SPI专有名词SpiDriverSpiChannelSpiChannelIdSpiChannelTypeSpiDataWidthSpiDefaultDataSpiEbMaxLengthSpiIbNBuffersSpiTransferStartSpiExternalDeviceSpiBaudrateSpiAutoCalcBaudParamsSpiCsIdentifierSpiCsPolaritySpiCsSelectionSpiDataSh…

前端工程师可以分成 4 种,你属于哪一种?

在这篇文章中&#xff0c;探讨四种常见的前端工程&#xff0c;1&#xff09;产品工程师&#xff0c;2&#xff09;UI 基建工程师&#xff0c;3&#xff09;设计师&#xff0c;4&#xff09;工具基建工程师&#xff0c;你属于哪一种&#xff1f; 产品工程师 产品工程师负责公司…

6.s081 学习实验记录(二)xv6 and unix utilities

文章目录一、boot xv6二、sleep三、pingpong四、primes五、find六、xargs该实验主要用来熟悉xv6以及其系统调用 一、boot xv6 实验目的&#xff1a; 启动xv6系统&#xff0c;并使用提供的命令ls&#xff0c;列出系统所有的文件ctrl p&#xff0c;打印当前运行的进程ctrl a…

Ubuntu22.04使用kubeadm安装k8s 1.26版本高可用集群

目录阿里云ACK集群的架构ACK实例的创建过程如下安装前的准备主机规划基线准备所有k8s master、worker节点安装kubeadmkubectlkubelet创建集群负载均衡器HAproxy安装keepalived 和haproxy配置haproxy配置keepalivedkubeadm部署第一台master节点Calico网络组件一键安装安装完成阿…

mPEG-Biotin,甲氧基-聚乙二醇-生物素科研实验用试剂

​​ 英文名称&#xff1a;mPEG-Biotin 中文名称&#xff1a;甲氧基-聚乙二醇-生物素 mPEG生物素可通过与链霉亲和素和抗生物素结合进行聚乙二醇化&#xff0c;具有高亲和力和特异性。生物素通过稳定的酰胺连接物与线性PEG结合。 提示&#xff1a;避免频繁的溶解和冻干&…