Welche Entwurfsmuster sind für die Änderung der Datenstruktur innerhalb von Ethereum Smart Contracts geeignet?

Ich bin auf der Suche nach Designmustern, die Menschen in Smart Contracts von Ethereum implementiert haben, um die Änderung von Datenstrukturen nach der Bereitstellung zu ermöglichen.

Angenommen, ich habe einen Vertrag, der eine Struktur enthält, die eine Adresse definiert. Sollte ich auf der ganzen Linie feststellen, dass ich der Adresse eine E-Mail-Adresseigenschaft hinzufügen möchte.

Ein etwas komplexeres Beispiel. Wenn ich zwei Eigenschaften eines Vertrags mit den Namen "Frage" und "Antwort" hätte und plötzlich mehrere mögliche Antworten haben möchte ... wie würde man vorgehen, um eine solche Änderung vorzunehmen?

Mein Problem / meine Sorge ist, dass Sie, wenn Sie beispielsweise über einen Browser mit diesem Vertrag kommunizieren, einfach den Vertrag aktualisieren könnten, auf den das Frontend verweist (nach dem Update). ABER wie lösen Sie das Problem der Aufrechterhaltung von Vertragsdaten über die Aktualisierung hinweg Einsätze..?

Danke

Einige der Antworten in diesem Beitrag könnten Ihnen helfen: ethereum.stackexchange.com/questions/2159/…

Antworten (4)

Sie müssen Folgendes berücksichtigen:

Dies muss von Anfang an geplant werden. Sie müssen Ihren Smart Contract unter Berücksichtigung der folgenden 5 Punkte entwerfen:

  1. Sie müssen gute Teststrategien und -taktiken haben . Denn die Kosten für die Aktualisierung Ihres Smart Contracts können Ihr Leben wirklich ruinieren.
  2. Halten Sie Ihre Smart Contracts modular und trennen Sie Regeln und Logik ziemlich von der Datenstruktur. Wenn Sie also etwas ändern müssen, ändern Sie nur den zugehörigen Vertrag und müssen nicht viele oder alle Verträge ändern.
  3. Sie sollten mit einem Notausschalter oder Leistungsschalter vorbereitet sein, um alle Vorgänge während einer Migration stoppen zu können. Weil Sie nicht in einer Situation sein möchten, in der Personen während und nach der Migration noch Daten in die alte Version des Smart Contracts aktualisieren/einfügen können.
  4. Sie sollten zuvor die Möglichkeit haben, alle Daten aus Ihrem Smart Contract auszulesen . Natürlich können Sie ein berechtigtes Lesen durchführen, indem Sie das Lesen aller Daten auf den Eigentümer oder einen anderen vertrauenswürdigen Benutzer oder sogar einen anderen Smart Contract beschränken. Sie müssen die alte Version Ihres Smart Contracts lesen und in die neue Version einfügen.
  5. Sie werden eine der folgenden Strategien verwenden, um mit Ihrem Smart Contract zu kommunizieren. Ich habe sie von Smart Contact Best Practices kopiert :

Unterbrochene Verträge aktualisieren

Der Code muss geändert werden, wenn Fehler entdeckt werden oder Verbesserungen vorgenommen werden müssen. Es ist nicht gut, einen Fehler zu entdecken, aber keine Möglichkeit zu haben, damit umzugehen

...

Es gibt jedoch zwei grundlegende Ansätze, die am häufigsten verwendet werden. Die einfachere der beiden ist ein Registrierungsvertrag, der die Adresse der neuesten Version des Vertrags enthält. Ein nahtloserer Ansatz für Vertragsbenutzer ist ein Vertrag, der Anrufe und Daten an die neueste Version des Vertrags weiterleitet.

Beispiel 1: Verwenden Sie einen Registrierungsvertrag, um die neueste Version eines Vertrags zu speichern

In diesem Beispiel werden die Anrufe nicht weitergeleitet, daher sollten Benutzer die aktuelle Adresse jedes Mal abrufen, bevor sie damit interagieren.

contract SomeRegister {
    address backendContract;
    address[] previousBackends;
    address owner;

    function SomeRegister() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner)
        _;
    }

    function changeBackend(address newBackend) public
    onlyOwner()
    returns (bool)
    {
        if(newBackend != backendContract) {
            previousBackends.push(backendContract);
            backendContract = newBackend;
            return true;
        }

        return false;
    }
}

Dieser Ansatz hat zwei Hauptnachteile:

  1. Nutzer müssen immer die aktuelle Adresse nachschlagen, wer das nicht tut, läuft Gefahr, eine alte Vertragsversion zu verwenden

  2. Sie müssen sich genau überlegen, wie Sie mit den Vertragsdaten umgehen, wenn Sie den Vertrag ersetzen

Der alternative Ansatz besteht darin, Anrufe und Daten per Vertrag an die neueste Version des Vertrags weiterzuleiten:

Beispiel 2: Verwenden Sie einen DELEGATECALL, um Daten und Anrufe weiterzuleiten

contract Relay {
    address public currentVersion;
    address public owner;

    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    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() {
        require(currentVersion.delegatecall(msg.data));
    }
}

Dieser Ansatz vermeidet die vorherigen Probleme, hat aber seine eigenen Probleme. Sie müssen äußerst vorsichtig damit sein, wie Sie Daten in diesem Vertrag speichern. Wenn Ihr neuer Vertrag ein anderes Speicherlayout als der erste hat, können Ihre Daten beschädigt werden. Außerdem kann diese einfache Version des Musters keine Werte von Funktionen zurückgeben, sondern sie nur weiterleiten, was ihre Anwendbarkeit einschränkt. ( Komplexere Implementierungen versuchen, dies mit Inline-Assemblercode und einer Registrierung von Rückgabegrößen zu lösen.)

Unabhängig von Ihrem Ansatz ist es wichtig, dass Sie Ihre Verträge aktualisieren können, da sie sonst unbrauchbar werden, wenn die unvermeidlichen Fehler darin entdeckt werden.

Ich habe dazu eine Story auf Medium mit dem Titel erstellt: Essential Design Consideration for Ethereum dApps (1): Upgradeable Smart Contracts

Wir könnten auch auf eine Lösung vom Typ VARIANT zurückgreifen (ja, gefällt mir auch nicht).
Haben Sie eine Zuordnung (uint => StorageItem).

  • Die uint ist eine Feld-ID.

  • Der StorageItem-Vertrag hätte einen Zeichenfolgenwert und einen ganzzahligen Typ. Der Typ würde es Ihnen ermöglichen, von der Zeichenfolge in den erforderlichen Endtyp zu konvertieren.

Eine Schwäche hier ist, dass die Solidität irgendwann neue Typen unterstützen wird, die wir nutzen möchten.

Ich nehme an, Sie könnten den Speicher wie hier vorgeschlagen in einem separaten Vertrag aufbewahren, aber die Struktur würde die Daten in einem JSON-Objekt (String) speichern. Anschließend können Sie alle gewünschten Modifikationen vornehmen (Modell erweitern oder ändern).

zum Beispiel:

{"address": "Some Street","phone": 123456}

würde werden

{"address": "Some Street","phone": 123456, "email":"someone@email.com"}

Sie können fehlende Werte auf der Clientseite oder im aufrufenden Vertrag behandeln.

Es gibt eine Reihe von Ansätzen, mit denen Sie a auf aktualisieren können , wobei der Status (Daten und Kontostand) mit derselben Adresse wie zuvor beibehalten wird.Contract1Contract2

Wie funktioniert das? Eine Möglichkeit besteht darin, einen Proxy-Vertrag mit einer fallbackFunktion zu verwenden, bei der jeder Methodenaufruf/trx an den Implementierungsvertrag (der die gesamte Logik enthält) delegiert wird.Geben Sie hier die Bildbeschreibung ein

Ein Delegiertenaufruf ähnelt einem regulären Aufruf, außer dass der gesamte Code im Kontext des Aufrufers (Proxy) und nicht des Aufgerufenen (Implementierung) ausgeführt wird. Aus diesem Grund überträgt eine Übertragung im Code des Implementierungsvertrags das Guthaben des Proxys, und alle Lese- oder Schreibvorgänge in den Vertragsspeicher lesen oder schreiben aus dem Speicher des Proxys.

Bei diesem Ansatz interagieren Benutzer nur mit dem Proxy-Vertrag und wir können den Implementierungsvertrag ändern, während wir denselben Proxy-Vertrag beibehalten.

Geben Sie hier die Bildbeschreibung ein

Die fallbackFunktion wird bei jeder Anfrage ausgeführt, leitet die Anfrage an die Implementierung um und gibt den resultierenden Wert zurück (unter Verwendung von Opcodes).

Dies war eine grundlegende Erklärung, die uns ausreicht, um mit erweiterbaren Verträgen zu arbeiten. Falls Sie sich eingehend mit dem Proxy-Vertragscode und verschiedenen Proxy-Mustern befassen möchten, lesen Sie diese Beiträge.

Wie kann ich aktualisierbare Smart Contracts schreiben?

OpenZeppelin bietet fantastische CLI-Tools und JS-Bibliotheken , die sich um alle oben genannten komplexen proxyVerträge kümmern, sie mit dem Implementierungsvertrag (Logik) verknüpfen und alle Verträge verwalten, die Sie mithilfe der CLI bereitstellen, um sofort einsatzbereit zu sein.

Das Einzige, was Sie tun müssen, ist, Ihre Verträge zu schreiben und die OpenZeppelin CLI oder Bibliotheken zu verwenden, um die Verträge bereitzustellen.

HINWEIS: Es gibt einige Einschränkungen , die Sie beachten sollten, in Bezug darauf, wie Sie Ihre Verträge schreiben und wie Sie sie aktualisieren sollten. Es gibt auch eine Reihe von Problemumgehungen für diese Einschränkungen in diesem Beitrag .