@@ -15,6 +15,121 @@ timezone: UTC+8
1515## Notes
1616
1717<!-- Content_START -->
18+ # 2025-08-16
19+
20+ 透明代理模式详解
21+ 一、代理模式简介
22+ 在以太坊的智能合约世界中,合约一旦部署便具有不可修改性。这种特性虽然为合约提供了强大的安全性,但在实际应用场景里,当我们需要修复合约中的bug或者其功能进行升级时,就会面临挑战。为了解决这个问题,代理模式应运而生。代理模式的核心思想以合约的逻辑与数据存储分离开来,从而实现合约的可升级性。
23+ 透明代理(Transparent Proxy)是代理模式中流行的一种,由OpenZeppelin提出并在行业内得到了广泛的应用。
24+ 二、透明代理的核心问题:函数选择器冲突
25+ 代理在模式实际应用中面临的核心挑战是函数冲突问题。当代理约定和逻辑约定中名称和参数都包含相同的函数时,就会产生冲突。例如,若逻辑约定和代理契约都包含upgradeTo(address)函数,那么调用时,系统将无法显式地执行哪个函数。
26+ 透明代理模式通过区分管理员和普通用户的调用,巧妙地解决了这个问题:
27+ - 对管理员的调用:直接在代理合约中执行。
28+ - 对普通用户的调用:转发到逻辑合约执行。
29+ 三、代码实现分析
30+ 3.1 代理合约实现
31+ 下面我们来详细分析TransparentProxy.sol关键实现部分:
32+ contract TransparentProxy is Proxy {
33+ // EIP - 1967 存储插槽
34+ bytes32 private constant _ IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
35+ bytes32 private constant _ ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
36+
37+ constructor(address _initImplementation, address _initAdmin) {
38+ // 设置逻辑合约地址
39+ assembly {
40+ sstore(_IMPLEMENTATION_SLOT, _initImplementation)
41+ }
42+ // 设置管理员地址
43+ assembly {
44+ sstore(_ADMIN_SLOT, _initAdmin)
45+ }
46+ }
47+
48+ // 其他函数...
49+ }
50+ 核心技术点
51+ 1.EIP - 1967 货架插槽
52+ :透明采用固定的货架来货架实现地址和管理员地址。这些特殊的货架位置遵循EIP - 1967 标准,能够确保不会与逻辑紧密的货架布局产生冲突。
53+ bytes32 private constant _ IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
54+ bytes32 private constant _ ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
55+ 这些值是通过以下公式计算得出的:
56+ // bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
57+ // bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)
58+ 2.内联存储访问存储:代理合约使用内联存储(assembly)直接操作存储,这样做是为了避免使用常规的Solidity存储变量,从而防止存储冲突。
59+ assembly {
60+ sstore(_ IMPLEMENTATION_SLOT, _ initImplementation)
61+ }
62+ 3.代理转发机制:_ implementation()函数继承自Proxy合约,其主要作用是提供逻辑合约的地址。
63+ function _ implementation() internal view override returns (address) {
64+ address impl;
65+ assembly {
66+ impl := sload(_ IMPLEMENTATION_SLOT)
67+ }
68+ return impl;
69+ }
70+ 当用户调用转发约定中不存在的函数时,fallback函数(在基类Proxy中实现)调用转发到逻辑约定。
71+ 4.升级机制:代理合约提供了一个管理员管理员使用的升级函数:
72+ function upgradeTo(address _ newImplementation) external {
73+ require(msg.sender == admin(), "Only admin can upgrade");
74+ assembly {
75+ sstore(_ IMPLEMENTATION_SLOT, _ newImplementation)
76+ }
77+ }
78+ 该函数只能由管理员调用,用于将代理合约指向新的逻辑合约实现。
79+ 3.2 逻辑合约设计
80+ 我们设计了两个版本的逻辑合约:
81+ TransparentLogicV1.sol
82+ contract TransparentLogicV1 is Initializable, OwnableUpgradeable {
83+ uint256 public value;
84+
85+ function setValue(uint256 _value) public virtual {
86+ value = _value;
87+ }
88+
89+ function getValue() public view virtual returns (uint256) {
90+ return value;
91+ }
92+
93+ function initialize(address initialOwner) public initializer {
94+ __Ownable_init(initialOwner);
95+ }
96+ }
97+ TransparentLogicV2.sol
98+ contract TransparentLogicV2 is TransparentLogicV1 {
99+ uint256 public newValue; // 新增状态变量
100+
101+ function setValue(uint256 _newValue) public virtual override {
102+ newValue = _newValue;
103+ }
104+
105+ function getValue() public view virtual override returns (uint256) {
106+ return value + newValue;
107+ }
108+ }
109+ 核心技术点
110+ 1.可初始化合约:逻辑合约使用Initializable替代构造函数,这是因为在代理模式下,逻辑合约的构造函数不会被执行。
111+ function initialize(address initialOwner) public initializer {
112+ __ Ownable_init(initialOwner);
113+ }
114+ 取消constructor构造函数,采用initialize初始化函数,可以在升级合约时自动设置初始化参数,而不是在合约创建时进行设置。
115+ 2.状态变量布局:在V2版本中,新增的状态变量newValue被添加到了原有变量之后,这样保持了存储结构的兼容性,是确保升级安全的关键。
116+ 四、透明代理使用流程
117+ 1.配置逻辑合约V1:首先配置第一个版本的逻辑合约。
118+ 2.配置透明代理合约:配置透明代理合约,将其指向V1的地址。
119+ 3.交互阶段:调用代理合约的地址,但使用V1的ABI进行交互。
120+ 4.升级准备:当需要进行升级时,部署V2逻辑合约。
121+ 5.合约升级:管理员调用合约的upgradeTo函数,将其指向V2地址。
122+ 6.继续交互:继续使用代理合约地址,但使用 V2 的 ABI 进行交互。
123+ 五、最佳实践与安全建议
124+ 1.状态设备:在升级合约时,不要修改、删除或重排现有状态设备,以确保存储设备的兼容性。
125+ 2.管理员权限分离:考虑使用多签钱包或DAO作为管理员,以增强权限管理的安全性。
126+ 3.初始化函数:确保初始化函数有适当的访问控制,防止未授权的初始化操作。
127+ 4.升级后验证:每次升级后,都要验证新功能是否正常工作,确保升级的稳定性。
128+ 5.存储或冲突规避:特定使用命名模式避免结构体存储冲突,保证持续的正常运行。
129+ 六、总结
130+ 透明代理模式是Solidity中实现合约可升级性的强大工具,它通过巧妙的设计成功解决了代理模式中的函数选择器冲突问题。深入了解其实现细节和工作原理,对于开发安全、可靠的可升级智能合约系统至关重要。
131+ 代理模式虽然为合约开发带来了灵活性,但同时也带来了复杂性和潜在风险。在实际应用中,开发者需要在可升级性和安全性之间进行权衡,严格遵循最佳实践,以保证系统的稳定性和安全性。
132+
18133# 2025-08-15
19134
20135UUPS 代理是基于 ERC-1967 标准设计的一种智能合约升级模式。与传统的透明代理模式(Transparent Proxy)不同,UUPS 将升级逻辑内置于实现合约,而不是代理合约本身。代理合约仅用于转发呼叫,所有逻辑由实现合约处理。
0 commit comments