Break versioning with registry

You have learned the registry pattern and if you look at it closely, it has a flaw when the company is malicious, use the exploit to steal users funds or just censor a user. Here is an example implementation of the registry pattern.

contract Registry is Ownable {

    event RegistryCreated(address addressOfRegistryContract,address from );

    mapping (address => address) public addresses;
    mapping (address => bool) public userPreferences;
    address[] public validImplementations;
    
  
    constructor() public {
        emit RegistryCreated(address(this),msg.sender);
    }
    function setImplementationAddress(address newAddress) public {
        require(newAddress != 0, "cant set value to 0");
        require(checkValidImplementationAddress(newAddress), "not a valid implementation address");
        addresses[msg.sender] = newAddress;
    }

    function checkValidImplementationAddress(address addr) internal view returns (bool) {
        for(uint i = 0; i < validImplementations.length; ++i) {
            if (addr == validImplementations[i]) return true;
        }
        return false;
    }

    function addImplementation(address toAdd) external onlyOwner {
        require(toAdd != 0);
        validImplementations.push(toAdd);
    } 
    
    function updatePreference(bool newPref) public {
        userPreferences[msg.sender] = newPref;
    }
}

contract Hello {
    address public me = 0;
    function sayHello() public pure returns(string) {
        return "Hello";
    }
}


contract HelloV2 is Hello {
    uint public abc = 1234;
    function sayHello() public pure returns (string) {
        return "hello v2";
    }
}

contract Proxy {
  
    bytes32 private constant REGISTRY_IMPLEMENTATION_ADDRESS_KEY = keccak256("Registry address key");
    bytes32 private constant DEFAULT_IMPLEMENTATION_ADDRESS_KEY = keccak256("default implementation address key");
    
    function initialize(address _defLogicContract) internal {
        require(_defLogicContract != 0);
        require(Address.isContract(_defLogicContract));

        Registry registry = new Registry();
        address regAddress = address(registry);
       
        bytes32 reg = REGISTRY_IMPLEMENTATION_ADDRESS_KEY;
        bytes32 impl = DEFAULT_IMPLEMENTATION_ADDRESS_KEY;
        //solium-disable-next-line security/no-inline-assembly
        assembly {
            sstore(reg, regAddress)
            sstore(impl, _defLogicContract)
        }

        registry.addImplementation(_defLogicContract);

    }
    
    constructor(address _log) public {
        initialize( _log);
    }
    
    function upgradeDefaultImplementation(address _i) public {
        require(_i != 0);
        require(Address.isContract(_i));
        bytes32 impl = DEFAULT_IMPLEMENTATION_ADDRESS_KEY;
        bytes32 regKey = REGISTRY_IMPLEMENTATION_ADDRESS_KEY;
        address registryImpl = 0;
      
        //solium-disable-next-line security/no-inline-assembly
        assembly {
            sstore(impl, _i)
            registryImpl := sload(regKey)
        }
        assert(registryImpl != 0);
        Registry reg = Registry(registryImpl);
        reg.addImplementation(_i);
    }

    function() public {
        
        bytes32 regImplKey = REGISTRY_IMPLEMENTATION_ADDRESS_KEY;
        bytes32 defImplKey = DEFAULT_IMPLEMENTATION_ADDRESS_KEY;
        address regImplAddress = 0;
        address defImplAddress = 0;
        //solium-disable-next-line security/no-inline-assembly
        assembly {
            regImplAddress := sload(regImplKey)
            defImplAddress := sload(defImplKey)
        }
    
        require(regImplAddress != 0 && defImplAddress != 0);

        Registry r = Registry(regImplAddress);
    
        if(r.userPreferences(msg.sender) == true) {
            // means that user may have a different address for implementation
            
            if ( r.addresses(msg.sender) != 0) {
                // they dont have it set, lets use default one
                defImplAddress = r.addresses(msg.sender);
            }
        }
    
    // at this point defImplAddress should not be 0
    
        assert(defImplAddress != 0);
    
        //solium-disable-next-line security/no-inline-assembly
        assembly {
            let ptr := mload(0x40)
            calldatacopy(ptr, 0, calldatasize)
            let result := delegatecall(gas, defImplAddress, ptr, calldatasize, 0, 0)
            let size := returndatasize
            returndatacopy(ptr, 0, size)

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

*https://github.com/ali2251/Upgradable-contracts/blob/master/contracts/Proxy.sol

Last updated