Smart Contract – Aktualisieren Sie die Implementierung mithilfe eines Relays mit DelegateCall

Ich weiß, dass intelligente Verträge unveränderlich sein sollen und das ist der springende Punkt, aber zu erwarten, dass jemand eine Logik implementiert, die sich vom ersten Tag an nie ändert (kein Upgrade oder keine Fehler), ist auch unrealistisch.
Daher habe ich über verschiedene Methoden gelesen, um diesen unveränderlichen Zustand zu umgehen. Eine beliebte Methode scheint die Verwendung von "delegateCall" mit einem Relay-Vertrag zu sein, aber ich habe Probleme damit, diese Methode tatsächlich zu verwenden, da ich kein Beispiel finden konnte.
Wäre jemand so freundlich, sich dieses einfache Beispiel anzusehen, das ich erstellt habe, und mir zu sagen, was ich falsch mache?
https://gist.github.com/fabdarice/d513d620d9355312d085c7a68e6c6118
Relais.sol

contract Relay {
  address public currentVersion;
  address public owner;
  mapping (address => uint) user_amounts;


  modifier onlyOwner() {
    if (msg.sender != owner) {
        throw;
    }
    _;
  }

  function Relay(address initAddr) {
    currentVersion = initAddr;
    owner = msg.sender; // this owner may be another contract with multisig, not a single contract owner
  }

  function changeContract(address newVersion) public
  onlyOwner()
  {
    currentVersion = newVersion;
  }

  function() {
    if(!currentVersion.delegatecall(msg.data)) throw;
  }
}  

Spende.sol :

contract Donation {
  mapping (address => uint) user_amounts;    


  /* DOES THIS METHODS MODIFY user_amounts of the Relay contract ??? */
  function sendDonation(uint n) {
    user_amounts[msg.sender] = user_amounts[msg.sender] + n
  }
}  

SpendeNeu.sol :

contract DonationNew {
  mapping (address => uint) user_amounts;

  function sendDonation(uint n) {
    user_amounts[msg.sender] = user_amounts[msg.sender] + n
  }

  function cancelDonation() {
    user_amounts[msg.sender] = 0
  }
}  

app.js :

// First, deploying Relay, then deploying Donation and retrieve Donation contract address in 'donation_contract_address'

// Then, linking Relay to my first version of my contract Donation 
Relay.deployed().then(function(contractInstance) {
   contractInstance.changeContract(donation_contract_address);
})

// Then, I want to call sendDonation from the Donation contract
// !!!!! I DON'T KNOW WHAT IS THE CORRECT WAY TO CALL THIS !!!!!!
Relay.deployed().then(function(contractInstance) {
   contractInstance.sendDonation(5) ;
})
// OR 
Relay.deployed().then(function(contractInstance) {
   contractInstance.currentVersion.delegateCall(sendDonation(5)) ;
})

// Now I want to update the Donation contract to add the cancelDonation function
// First I deploy the new contract DonationNew and retrieve it's address in 'donation_new_contract_address'
Relay.deployed().then(function(contractInstance) {
   contractInstance.changeContract(donation_new_contract_address);
})

// are the state variables still available from the old contract to the new one?

// Then if I want to call the new function : 
Relay.deployed().then(function(contractInstance) {
   contractInstance.cancelDonation() ;
})

PS: Ich weiß, dass diese obige Methode es uns ermöglicht, einen "aktualisierbaren Vertrag" zu erstellen, falls wir unsere Logik (Funktionen usw.) aktualisieren müssen, aber es erlaubt uns nicht, unsere Zustandsvariablenstruktur zu ändern/hinzuzufügen. Gibt es hierfür auch einen Workaround?

Vielen Dank, ich freue mich, Teil dieser Community zu sein!

Antworten (2)

Dies ist nur eine Antwort auf Ihre PS, da ich Solidity noch nicht gut kenne, um herauszufinden, was mit Ihrem Code nicht stimmt.

Wenn Sie die Möglichkeit haben möchten, Code zu aktualisieren und gleichzeitig Speicher zu behalten, könnten Sie daran denken, Speicher und Logik zu trennen. Haben Sie einen dedizierten Speichervertrag, der Schreibaufrufe von vertrauenswürdigen Adressen akzeptiert (z. B. die Logikverträge). Alle wichtigen Speicher sollten mit diesem verbunden werden. Sie sollten auch versuchen, ihn so flexibel wie möglich zu gestalten, damit er wahrscheinlich nicht aktualisiert werden muss.

Dieser Artikel enthält ein Beispiel sowie viele andere Vorschläge zum Schreiben von aktualisierbaren Smart Contracts:

https://blog.colony.io/writing-upgradeable-contracts-in-solidity-6743f0eecc88

Hier ist ein Beispiel für erweiterbare Bibliotheken.

https://github.com/kyriediculous/knuckles/tree/master/contracts/contracts

Ich arbeite gerade daran, es zu kürzen.

Es gibt ein zentrales Register, um die verwendeten Bibliotheksadressen zu verfolgen. Wenn Sie diese ändern, wird die Adresse geändert, an die die Proxys delegieren.

Der Proxy ist über eine Schnittstelle der Bibliothek mit dem Speicher verbunden. Dadurch werden die Anrufdaten erstellt, um sie an den Proxy zu senden, der sie dann an die Bibliothek delegiert.

Wenn Sie die Bibliothek aktualisieren möchten, stellen Sie sie erneut bereit und ändern die Adresse in der zentralen Registrierung.

Hier ist ein abgespecktes Beispiel, ein Blogbeitrag folgt in Kürze.

pragma solidity ^0.4.23;

contract Registry {
  mapping (bytes32 => address) public libraries;
  mapping (bytes32 => address) public contracts;

    function addLibrary(bytes32 _name, address _lib) external {
    require(libraries[_name] == address(0), "LIBRARY_ALREADY_EXISTS");
    require(_lib != address(0), "INSERT_VALID_LIBRARY_ADDRESS");
    libraries[_name] = _lib;
  }

  function addContract(bytes32 _name, address _contract) external {
    Enabled(_contract).setCMCAddress(address(this));
    contracts[_name] = _contract;
  }
}

interface ContractProvider {
    function libraries(bytes32 _name) external view returns (address);
    function contracts(bytes32 _name) external view returns (address);
}

contract Enabled {
  address public CMC;
  function setCMCAddress(address _CMC) external {
    if (CMC != 0x0 && msg.sender != CMC) {
        revert();
    } else {
        CMC = _CMC;
    }
  }
}

contract setXproxy is Enabled {
  function () payable public {
    address _impl =  ContractProvider(CMC).libraries('setXlib');
    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) }
    }
  }
}

library setXinterface {
    struct X {
        uint x;
    }

    function setX(X storage _X, uint _x) external;
}

library setXlib {
    function setX(setXinterface.X storage _X, uint _x) external {
        _X.x = _x;
    }
}

contract setXstorage is Enabled {
    using setXinterface for setXinterface.X;
    setXinterface.X X;
    function setX(uint _x) external {
        X.setX(_x);
    }

    function getX() external view returns (uint) {
      return X.x;
    }
}