理解 delegatecall 和透明代理:智能合约升级的关键技术

理解 delegatecall 和透明代理:智能合约升级的关键技术

在区块链世界中,智能合约一旦部署便不可更改。然而,随着时间的推移,智能合约可能需要修复漏洞或添加新功能。因此,合约的可升级性变得至关重要。

什么是 delegatecall

delegatecall 是 Solidity 中的一种低级别函数调用,用于将当前合约的上下文传递给另一个合约。这意味着被调用合约的代码在调用者合约的存储上下文中执行。

类比理解

可以把 delegatecall 想象成一个演员(合约A)在另一个演员(合约B)的剧本(代码)指导下表演。虽然合约A执行的是合约B的代码,但使用的是合约A的道具和场景(存储和上下文)。

delegatecall 的语法

(bool success, bytes memory data) = target.delegatecall(data);

其中,target 是被调用的合约地址,data 是调用的数据。

使用场景

  1. 代理合约模式(Proxy Pattern)
    在这个模式中,一个合约(代理合约)代表另一个合约(逻辑合约)执行代码。通过 delegatecall,代理合约能够将调用传递给逻辑合约,从而实现逻辑合约的功能。这种模式使得合约可以在保持地址不变的情况下进行升级。

什么是透明代理(Transparent Proxy Pattern)?

管理员变为工具人,仅能调用代理合约的可升级函数对合约升级,不能通过回调函数调用逻辑合约。其它用户不能调用可升级函数,但是可以调用逻辑合约的函数。
避免函数选择器冲突。

透明代理模式的结构

  1. 代理合约(Proxy Contract):存储合约地址和数据,通过 delegatecall 调用逻辑合约。
  2. 逻辑合约(Logic Contract):包含实际的业务逻辑,可以随时更换新的逻辑合约以实现升级。

类比理解

可以把透明代理模式想象成一个剧院(代理合约),剧院有一个固定的地址,观众(用户)总是去这个剧院看戏。而剧院里上演的剧目(逻辑合约)可以更换,由剧院经理(代理管理员)决定更换哪部剧。
其实就是设计模式中的代理模式。

透明代理模式的优势

  1. 可升级性:允许更换逻辑合约,修复漏洞或添加新功能,而无需更换代理合约地址。
  2. 透明性:用户与代理合约交互时,不需要知道背后的逻辑合约,实现无缝升级。
  3. 灵活性:可以通过升级逻辑合约来适应不同的业务需求。

DEMO

代理合约

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;
    }
}

逻辑合约

// 旧逻辑合约
contract Logic1 {
    // 状态变量和proxy合约一致,防止插槽冲突
    address public implementation; 
    address public admin; 
    string public words; // 字符串,可以通过逻辑合约的函数改变

    // 改变proxy中状态变量,选择器: 0xc2985578
    function foo() public{
        words = "old";
    }
}

// 新逻辑合约
contract Logic2 {
    // 状态变量和proxy合约一致,防止插槽冲突
    address public implementation; 
    address public admin; 
    string public words; // 字符串,可以通过逻辑合约的函数改变

    // 改变proxy中状态变量,选择器:0xc2985578
    function foo() public{
        words = "new";
    }
}
posted @ 2021-05-20 18:57  sorachannel  阅读(1503)  评论(0)    收藏  举报