Aufrüstbare Proxy-Bibliothek wirft jede Funktion auf, wie man Proxy und Hauptvertrag verbindet

Ich versuche, eine Proxy-Bibliothek durch die von openZeppelin erforschten Techniken mit Proxys zu implementieren.

Ich habe 4 Verträge 1. Bibliothek mit Logik 2. Bibliothek, die die Schnittstelle für die Logik enthält 3. Vertrag, der als Proxy fungiert 4. Hauptvertrag, der den Proxy über eine Schnittstelle aufruft, die an die Bibliothek delegiert

Das Problem, das ich habe, denke ich. Ist das, dass mein Hauptvertrag seine Anrufe nicht an den Proxy sendet, obwohl er die Schnittstelle bei der Bereitstellung verknüpft hat?

Ich habe eine Testdatei erstellt, die die Verträge bereitstellt und die Bibliotheken verknüpft. Ich kann bestätigen, dass die Bibliothek an den Proxy-Vertrag gebunden ist.

Wie kann ich People.sol (Hauptvertrag) eine Funktion auf dem PeopleInterface aufrufen lassen, die die erforderlichen Anrufdaten erstellt, um sie an den Delegiertenanruf im Proxy auszugeben?

const PeopleProxy = artifacts.require('OwnedUpgradeabilityProxy')
const PeopleLib = artifacts.require('PeopleLib')
const People = artifacts.require('People') //storage

//FIRST DEPLOYMENT:
//1. Deploy PeopleLib
//2. Deploy PeopleProxy
//3. Link implementation (PeopleLib) to proxy --> For normal contracts
//4. Link PeopleInterface to PeopleStorage
//5. Deploy PeopleStorage

//TO UPDATE:
//1. Deploy new version of PeopleLib
//2. Call upgradeTo on PeopleProxy

contract('TestProxy', (accounts) => {
  describe('Deployment & initial test', () => {
    let peopleLib, peopleProxy, people
    before(async () => {
      peopleLib = await PeopleLib.new()
      peopleProxy = await PeopleProxy.new({from: accounts[0]})
      People.link('PeopleInterface', peopleProxy.address)
      people = await People.new()
      await peopleProxy.upgradeTo(peopleLib.address)
    })
    it('Should have implemented our library address in proxy', async () => {
      console.info(await peopleProxy.implementation(), peopleLib.address)
      assert.equal(await peopleProxy.implementation(), peopleLib.address, "Addresses not equal")
    })
    it('Should register a new user', async () => {
      await people.registerUser("Nico", "nico@did.com", "Design is dead", "", accounts[0])
    })
  })
})

Das sind meine Verträge:

pragma solidity ^0.4.23;

library PeopleInterface {
    struct Person {
      string name;
      string email;
      string company;
      string avatar;
      address wallet;
      address[] groups;
      mapping (address => bytes32[]) personBounties; //groupaddress -> bounty id's use controller.getBounty(_groupAddress, bountyId)
    }

    struct People {
      mapping (address => Person) people;
    }

    function registerUser(People storage _people, string _name, string _email, string _company, string _avatar, address _sender) external;
    function updateUser(People storage _people, string _name, string _email, string _company, string _avatar, address _sender) external;
    function getUser(People storage _people, address _address) external view returns (string, string, string, string, address, address[]);
    function addGroup(People storage _people, address _group, address _sender) external;
    function leaveGroup(People storage _people, address _group, address _sender) external;
    function addBounty(People storage _people, address _group, bytes32 _index, address _sender) external;
    function getUserBountiesByGroup(People storage _people, address _group, address _sender) external view returns (bytes32 []);
}

library PeopleLib {

  event logRegistered(address indexed _wallet, string _name, string _email, string _company);
  event logUpdateProfile(address indexed _wallet, string _email, string _name, string _company, string _avatar);

  function registerUser (PeopleInterface.People storage _people, string _name, string _email, string _company, string _avatar, address _sender) external {
      _people.people[_sender].name = _name;
      _people.people[_sender].email = _email;
      _people.people[_sender].company = _company;
      _people.people[_sender].avatar = _avatar;
      _people.people[_sender].wallet = _sender;
      emit logRegistered(_sender, _name, _email, _company);
  }

  function updateUser (PeopleInterface.People storage _people, string _name, string _email, string _company, string _avatar, address _sender) external {
    _updateName(_people, _name, _sender);
    _updateEmail(_people, _email, _sender);
    _updateCompany(_people, _company, _sender);
    _updateAvatar(_people, _avatar, _sender);
     emit logUpdateProfile(_sender, _email, _name, _company, _avatar);
  }

  function _updateName (PeopleInterface.People storage _people, string _name, address _sender) public {
    bytes memory name = bytes(_name);
    if (name.length > 0) _people.people[_sender].name = _name;
  }

  function _updateEmail (PeopleInterface.People storage _people, string _email, address _sender) public {
    bytes memory email = bytes(_email);
    if (email.length > 0) _people.people[_sender].email = _email;
  }

  function _updateCompany (PeopleInterface.People storage _people, string _company, address _sender) public {
      bytes memory company = bytes(_company);
      if (company.length > 0) _people.people[_sender].company = _company;
  }

  function _updateAvatar (PeopleInterface.People storage _people, string _avatar, address _sender) public {
      bytes memory avatar = bytes(_avatar);
      if (avatar.length > 0) _people.people[_sender].avatar = _avatar;
  }

  function getUser (PeopleInterface.People storage _people, address _address) external view returns (string, string, string, string, address, address[]) {
      address[] memory _groups = _getGroups(_people, _address);
      return (_people.people[_address].name, _people.people[_address].email, _people.people[_address].company, _people.people[_address].avatar, _address, _groups);
  }

  function _getGroups(PeopleInterface.People storage _people, address _address) public view returns (address[]) {
    return _people.people[_address].groups;
  }

  function addGroup (PeopleInterface.People storage _people, address _group, address _sender) external {
      _people.people[_sender].groups.push(_group);
  }

  function leaveGroup (PeopleInterface.People storage _people, address _group, address _sender) external {
    for (uint i = 0; i < _people.people[_sender].groups.length; i ++) {
      if ( _group == _people.people[_sender].groups[i] ) {
        _people.people[_sender].groups = _deleteAddress(_people.people[_sender].groups, i);
      }
    }
  }

  function _deleteAddress(address[] _array, uint _index) public pure returns (address[]) {
    address[] memory arrayNew = new address[](_array.length-1);
    assert(_index < _array.length);
    for (uint i = 0; i<_array.length-1; i++){
      if(i != _index && i<_index){
        arrayNew[i] = _array[i];
      } else {
        arrayNew[i] = _array[i+1];
      }
    }
    delete _array;
    return arrayNew;
  }

  function addBounty (PeopleInterface.People storage _people, address _group, bytes32 _index, address _sender) external {
    _people.people[_sender].personBounties[_group].push(_index);
  }

  function getUserBountiesByGroup (PeopleInterface.People storage _people, address _group, address _sender) external view returns (bytes32 []) {
    return _people.people[_sender].personBounties[_group];
  }
}

contract People {
  using PeopleInterface for PeopleInterface.People;
  PeopleInterface.People people;

  event logRegistered(address indexed _wallet, string _name, string _email, string _company);
  event logUpdateProfile(address indexed _wallet, string _email, string _name, string _company, string _avatar);

  function registerUser(string _name, string _email, string _company, string _avatar, address _sender) external   {
    return people.registerUser(_name, _email, _company, _avatar, _sender);
  }

  function updateUser(string _name, string _email, string _company, string _avatar, address _sender) external   {
    return people.updateUser(_name, _email, _company, _avatar, _sender);
  }

  function getUser(address _wallet) external view returns (string, string, string, string, address, address[]) {
    return people.getUser(_wallet);
  }

  function addGroup(address _group, address _sender) external   {
    return people.addGroup(_group, _sender);
  }

  function leaveGroup(address _group, address _sender) external   {
    return people.leaveGroup(_group, _sender);
  }

  function addBounty(address _group, bytes32 _index, address _sender) external {
    return people.addBounty(_group, _index, _sender);
  }
}

pragma solidity ^0.4.23;

/**
 * @title Proxy
 * @dev Gives the possibility to delegate any call to a foreign implementation.
 */
contract Proxy {
  /**
  * @dev Tells the address of the implementation where every call will be delegated.
  * @return address of the implementation to which it will be delegated
  */
  function implementation() public view returns (address);

  /**
  * @dev Fallback function allowing to perform a delegatecall to the given implementation.
  * This function will return whatever the implementation call returns
  */
  function () payable public {
    address _impl = implementation();
    require(_impl != address(0));

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

contract UpgradeabilityProxy is Proxy {
  /**
   * @dev This event will be emitted every time the implementation gets upgraded
   * @param implementation representing the address of the upgraded implementation
   */
  event Upgraded(address indexed implementation);

  // Storage position of the address of the current implementation
  bytes32 private constant implementationPosition = keccak256("org.zeppelinos.proxy.implementation");

  /**
   * @dev Constructor function
   */
  function UpgradeabilityProxy() public {}

  /**
   * @dev Tells the address of the current implementation
   * @return address of the current implementation
   */
  function implementation() public view returns (address impl) {
    bytes32 position = implementationPosition;
    assembly {
      impl := sload(position)
    }
  }

  /**
   * @dev Sets the address of the current implementation
   * @param newImplementation address representing the new implementation to be set
   */
  function setImplementation(address newImplementation) internal {
    bytes32 position = implementationPosition;
    assembly {
      sstore(position, newImplementation)
    }
  }

  /**
   * @dev Upgrades the implementation address
   * @param newImplementation representing the address of the new implementation to be set
   */
  function _upgradeTo(address newImplementation) internal {
    address currentImplementation = implementation();
    require(currentImplementation != newImplementation);
    setImplementation(newImplementation);
    emit Upgraded(newImplementation);
  }
}

contract OwnedUpgradeabilityProxy is UpgradeabilityProxy {
  /**
  * @dev Event to show ownership has been transferred
  * @param previousOwner representing the address of the previous owner
  * @param newOwner representing the address of the new owner
  */
  event ProxyOwnershipTransferred(address previousOwner, address newOwner);

  // Storage position of the owner of the contract
  bytes32 private constant proxyOwnerPosition = keccak256("org.zeppelinos.proxy.owner");

  /**
  * @dev the constructor sets the original owner of the contract to the sender account.
  */
  function OwnedUpgradeabilityProxy() public {
    setUpgradeabilityOwner(msg.sender);
  }

  /**
  * @dev Throws if called by any account other than the owner.
  */
  modifier onlyProxyOwner() {
    require(msg.sender == proxyOwner());
    _;
  }

  /**
   * @dev Tells the address of the owner
   * @return the address of the owner
   */
  function proxyOwner() public view returns (address owner) {
    bytes32 position = proxyOwnerPosition;
    assembly {
      owner := sload(position)
    }
  }

  /**
   * @dev Sets the address of the owner
   */
  function setUpgradeabilityOwner(address newProxyOwner) internal {
    bytes32 position = proxyOwnerPosition;
    assembly {
      sstore(position, newProxyOwner)
    }
  }

  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function transferProxyOwnership(address newOwner) public onlyProxyOwner {
    require(newOwner != address(0));
    emit ProxyOwnershipTransferred(proxyOwner(), newOwner);
    setUpgradeabilityOwner(newOwner);
  }

  /**
   * @dev Allows the proxy owner to upgrade the current version of the proxy.
   * @param implementation representing the address of the new implementation to be set.
   */
  function upgradeTo(address implementation) public onlyProxyOwner {
    _upgradeTo(implementation);
  }

  /**
   * @dev Allows the proxy owner to upgrade the current version of the proxy and call the new implementation
   * to initialize whatever is needed through a low level call.
   * @param implementation representing the address of the new implementation to be set.
   * @param data represents the msg.data to bet sent in the low level call. This parameter may include the function
   * signature of the implementation to be called with the needed payload
   */
  function upgradeToAndCall(address implementation, bytes data) payable public onlyProxyOwner {
    upgradeTo(implementation);
    require(this.call.value(msg.value)(data));
  }
}

Antworten (1)

Anstelle der unstrukturierten Speicherung habe ich ein anderes Speicherprinzip (ewige Speicherung) verwendet. Dank an Maraoz: https://github.com/maraoz/solidity-proxy

Es funktioniert jetzt, da ich einige Dinge in meiner Testdatei geändert habe.

Das ist der neue Proxy:

pragma solidity ^0.4.23;

import './Ownable.sol';

contract ProxyStorage is Ownable {
  address public lib;

  constructor (address _newLib) public {
    replace(_newLib);
  }

  function replace(address _newLib) public onlyOwner /* onlyDAO */ {
    lib = _newLib;
  }
}
/**
 * @title Proxy
 * @dev Gives the possibility to delegate any call to a foreign implementation.
 */
contract Proxy {
  /**
  * @dev Fallback function allowing to perform a delegatecall to the given implementation.
  * This function will return whatever the implementation call returns
  */
  function () payable public {
    ProxyStorage proxystorage = ProxyStorage(0x1111222233334444555566667777888899990000);
    address _impl = proxystorage.lib();
    require(_impl != address(0));

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

Und das ist die neue Testdatei:

contract('TestProxy', (accounts) => {
  describe('Deployment & initial test', () => {
    let peopleLib, proxystorage, peopleProxy, people
    before(async () => {
      peopleLib = await PeopleLib.new()
      proxystorage = await ProxyStorage.new(peopleLib.address)
      PeopleProxy.unlinked_binary = PeopleProxy.unlinked_binary.replace('1111222233334444555566667777888899990000', proxystorage.address.slice(2))
      peopleProxy = await PeopleProxy.new()
      People.link('PeopleInterface', peopleProxy.address)
      people = await People.new()
    })
    it('Should have implemented our library address in proxy', async () => {
      console.info(await proxystorage.lib(), peopleLib.address)
      assert.equal(await proxystorage.lib(), peopleLib.address, "Addresses not equal")
    })
    it('Should register a new user', async () => {
      await people.registerUser("Nico", "nico@did.com", "Design is dead", "", accounts[0])
      console.info(await people.getUser(accounts[0]))
    })
  })
})

Was zurückkehrt

  Contract: TestProxy
    Deployment & initial test
0x32c836fbd91e88e2843cfad7d5977f0c0697629e 0x32c836fbd91e88e2843cfad7d5977f0c0697629e
      √ Should have implemented our library address in proxy (102ms)
[ 'Nico',
  'nico@did.com',
  'Design is dead',
  '',
  '0x84aff42e44e9b1a278feb8512e463285cd1118b2',
  [] ]
      √ Should register a new user (313ms)


  2 passing (1s)