diff --git a/Arvid-io.md b/Arvid-io.md index 9982a5c5..e68ce88e 100644 --- a/Arvid-io.md +++ b/Arvid-io.md @@ -2106,6 +2106,60 @@ Copy 此时amount为10,value为11,amount<=value,转账成功。 +什么是选择器冲突? + +选择器冲突是指不同的函数对应的函数选择器相同。 + +// 选择器冲突的例子 +contract Foo { + function burn(uint256) external {} + function collate_propagate_storage(bytes16) external {} +} +上面的代码中,函数burn()和collate_propagate_storage()的选择器都为0x42966c68,这种情况被称为“选择器冲突”。在这种情况下,EVM无法通过函数选择器分辨用户调用哪个函数,因此该合约无法通过编译。 + +由于代理合约和逻辑合约是两个合约,就算他们之间存在“选择器冲突”也可以正常编译,这可能会导致很严重的安全事故。举个例子,如果逻辑合约的a函数和代理合约的升级函数的选择器相同,那么管理人就会在调用a函数的时候,将代理合约升级成一个黑洞合约,后果不堪设想。 + +而透明代理就是通过限制管理员和普通用户的权限来解决选择器冲突的。 + +透明代理(Transparent Proxy)- 可以解决代理合约的选择器冲突(Selector Clash)问题。 + +透明代理解决冲突的思路 + +将管理员设置为仅能调用代理合约的升级函数,且不能通过回调函数调用逻辑合约。 + +其他用户只能通过回调函数调用逻辑合约,不能调用逻辑合约的升级函数。 + +// 透明可升级合约的教学代码,不要用于生产。 +contract TransparentProxy { + address implementation; // logic合约地址 + address admin; // 管理员 + string public words; // 字符串,可以通过逻辑合约的函数改变 + + // 构造函数,初始化admin和逻辑合约地址 + constructor(address _implementation){ + admin = msg.sender; + implementation = _implementation; + } + + // fallback函数,将调用委托给逻辑合约 + // 不能被admin调用,避免选择器冲突引发意外 + fallback() external payable { + require(msg.sender != admin); + (bool success, bytes memory data) = implementation.delegatecall(msg.data); + } + + // 升级函数,改变逻辑合约地址,只能由admin调用 + function upgrade(address newImplementation) external { + if (msg.sender != admin) revert(); + implementation = newImplementation; + } +} +学习总结 + +用户透明性:用户与代理合约交互时,感知不到背后逻辑合约的升级,所有调用都被透明地委托给最新的逻辑合约。 + +开发者透明性:只有非管理员角色(即普通用户)可以通过代理合约调用逻辑合约,而管理员角色只能进行合约升级。这种权限控制确保了合约升级过程对普通用户是“透明”的,避免了他们调用到升级相关的函数。 + 以上。