Solidity-delegateCall插槽冲突分析与解决

delegatecall插槽冲突

1.了解delegatecall的插槽冲突原理 首先严格来说应该不是插槽得冲突问题,而是本身delegate得运行机制问题,因为所有得delegatecall得运行环境都是在当前得被委托得合约里面

2.本身delegatecall他机制运行环境就是这样,所有修改得状态都会根据委托目标地址修改得状态变量内存位置,修改到被委托得合约里面。此委托调用无法修改目标地址得任何状态

3.如何解决

  • 一种解决方案是存储代理合约的数据时避开通常的Solidity存储布局机制,使用 EVM的sstore和sload指令来读取或写入数据到伪随机插槽中
  • 例如,使用由keccak256(my.proxy.version)返回的值作为插槽号。这样我们就可以避免槽位的冲突。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/utils/StorageSlotUpgradeable.sol";
/**
 * @title InitializedProxy
 * @author Anna Carroll
 */
contract InitializedProxy {
    // address of logic contract
    // slot bytes32(uint256(keccak256('EIP1967.PROXY.CONFTI.IMPLEMENTATION')) - 1)
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x5f62ce3c9aebd463c7a36ab1b244d2bb94f07a2c13889b3b687940ebc467b9b3;

    // ======== Constructor =========
    constructor(
        address logic,
        bytes memory initializationCalldata
    ) { 
        require(logic != address(0),"Proxy :: Logical contracts cannot be zero addresses");
        StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = logic;
        // Delegatecall into the logic contract, supplying initialization calldata
        (bool _ok, bytes memory returnData) =
            logic.delegatecall(initializationCalldata);
        // Revert if delegatecall to implementation reverts
        require(_ok, string(returnData));
    }
     

    // ======== Fallback =========

    fallback() external payable {
        address _impl = StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
        assembly {
            let ptr := mload(0x40)
            calldatacopy(ptr, 0, calldatasize())
            let result := delegatecall(gas(), _impl, ptr, calldatasize(), 0, 0)
            let size := returndatasize()
            returndatacopy(ptr, 0, size)

            switch result
                case 0 {
                    revert(ptr, size)
                }
                default {
                    return(ptr, size)
                }
        }
    }

    // ======== Receive =========

    receive() external payable {} // solhint-disable-line no-empty-blocks
}
  • 另一种方法是使用相同的存储布局和高级别的数据争议解决,就像这个github仓库中的实现。 使用结构体\

问题模拟

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

/** 
 * @title Ballot
 * @dev Implements voting process along with vote delegation
 */
  
contract logic  {
   string public name;
   uint256 public age;
 
   function setName(string memory _name) public {
         name =_name;
   } 
   
}

contract prx{
   string public amount;
   uint256 public higth;
   uint256 public test;
   function _delegatecall(address _traget)public {
       (bool result ,bytes memory  _data)  = 
            _traget.delegatecall(abi.encodeWithSignature("setName(string)","zk"));
   }

}

最后调用setName方法,修改name 会修改到prx得amount上面,因为name得内存位置是0,amount也是0位置。 

你可能感兴趣的:(以太坊)