Vertragsgestaltung durch Vertrag -> Korrektheit und maximaler Gasverbrauch

Diese Frage ist eine allgemeine Designfrage, die sich auf die Vertragskorrektheit bezieht , und die hier aufgeführten Funktionen sind nur Beispiele.

Während ich auf Ethereum entwickle, wird mir klar, dass es relativ einfach sein kann, einen Vertrag falsch zu gestalten, der sich selbst blockiert, weil notwendige Schlüsselfunktionen plötzlich nicht mehr aufrufbar sind, weil ihre Ausführung plötzlich das Gaslimit überschreitet.

Das führt mich zu der wahrscheinlich schwierigen Frage:

Wie kann ich behaupten , dass eine bestimmte Funktion nie mehr als eine bestimmte Menge Gas verbraucht? Dies scheint eine entscheidende Notwendigkeit für die Vertragskorrektheit zu sein.

Zum Beispiel

function unbound() returns (uint256) {
    uint256 total = 0;
    for (uint i=0; i < users.length; i++) {
        total+= users[i].value;
    }
    return total;
}

Das Aufrufen dieser Funktion hat undeterministische Gaskosten und könnte irgendwann leicht nicht mehr aufrufbar werden. Natürlich ist es in einem so einfachen Beispiel wie diesem einfach, diese Summe umzugestalten und nicht in einer Schleife, sondern auf andere Weise zu berechnen. Aber Schleifen lassen sich nicht immer vermeiden. Die Frage ist, wie schreibt man in diesem Fall korrekte Funktionen?

Beispielsweise könnte das System so konzipiert sein, dass es nie mehr als 6 Benutzer gibt. In diesem Fall ist die Schleife gebunden und die Ausführungskosten deterministisch. Wenn ich also Folgendes habe:

function bound() returns (uint256) {
    uint256 total = 0;
    assert(users.length < 6);
    for (uint i=0; i < users.length; i++) {
        total+= users[i].value;
    }
    return total;
}

Diese Funktion verwendet eine Schleife, hat aber eine deterministische Obergrenze für den Gasverbrauch.

A: Wie kann ich die maximale Gasmenge berechnen, die beim Aufrufen dieser Funktion verbraucht wird?

B: Wie kann ich dieses Maximum den Kunden der Funktion mitteilen?

Kann ich sowas irgendwie machen:

function bound() returns (uint256) {
    uint256 total = 0;
    for (uint i=0; i < users.length; i++) {
        total+= users[i].value;
    }
    assert(gasConsumend < 1234);
    return total;
}

?

Schließlich schreiben wir Verträge, und Vor- und Nachbedingungen sind ein integraler Bestandteil von Design by Contract . Wir können Vorbedingungen mit require() und Nachbedingungen mit assert() schreiben - aber für die Korrektheit auf Ethereum scheint es auch notwendig zu sein, einen maximalen Gasverbrauch anzugeben. Wie?

Antworten (1)

Ich befürchte, dass es darauf keine narrensichere Antwort geben wird, aus dem einfachen Grund, dass sich im Laufe von Forks die Gaskosten für bestimmte Opcodes ändern können (wie es vor einiger Zeit getan wurde, um Spam-Angriffe zu stoppen).

Aufgrund des Vorstehenden kann ein fest codierter Gascheck auf Dauer nicht mehr gültig sein.

Wenn es um eine Schleifenfunktion geht, ist es eine praktikable Option, zu verlangen, dass Ihre Eingabe unter einer bestimmten Länge liegt. Wenn Sie die Eingabe kennen, können Sie den eth_estimateGasAufruf außerdem verwenden, um einen "Trockenlauf" der Transaktion durchzuführen und das verbrauchte Gas zu schätzen, so dass MyEtherWallet, Metamask usw. manchmal automatisch das maximale Gasfeld füllen.

Beachten Sie, dass die Gasschätzung nicht perfekt ist, insbesondere bei Transaktionen, die sich in Abhängigkeit von externen Faktoren ändern (z. B. Zeitstempel oder ungerade/gerade Blockhashs).

Leider ist es oft nicht möglich, die Array-Längen zu begrenzen. Wenn Ihr Array eine Größe überschreiten muss, bei der es nicht möglich ist, es in einem TX zu durchlaufen, besteht eine Alternative darin, Ihre Funktion neu zu erstellen, sodass sie eine Liste von Indizes als Parameter akzeptiert und nur auf diese Indizes in dieser Transaktion zugreift. Dies kann Ihnen ermöglichen, Array-Inhalte zu aktualisieren/einige Operationen stückweise im Laufe einiger Transaktionen auszuführen.

Beispielsweise könnte eine Summenvariable auf Vertragsebene deklariert werden, und ein Array mit 1 Million Elementen könnte über den Verlauf von 5000 Transaktionen summiert werden, die jeweils 20 Elemente addieren.

Am Ende vermute ich, dass eine sorgfältige Vertragsgestaltung der Schlüssel zur Vermeidung der Situationen ist, und ein gutes Wallet-/Frontend-Design kann den Endbenutzern mitteilen, wenn eine Aktion das Blockgaslimit überschreitet, und sie auffordern, ihre Eingabegröße zu reduzieren.

aber eth_estimateGas ist eine Funktion von geth, keine Funktion von EVM, auf die irgendwie durch Verträge zugegriffen werden kann, richtig? Es scheint, dass wenn sich die Gaskosten plötzlich ändern können, dies bedeuten würde, dass es keinen einzigen Vertrag gibt, der seine Richtigkeit gewährleisten kann (in dem Sinne, dass er ausführbar bleibt). Dies scheint ein extremer Designfehler einer Plattform zu sein, die Sicherheit und Korrektheit durch 'Verträge' bieten soll...
Die Gasschätzung sollte von Wallets/Front-End-Clients verwendet werden, um Benutzer daran zu hindern, offensichtlich falsche Transaktionen durchzuführen. Während sich die Gaskosten ändern können, ist dies außerdem äußerst selten, und eine Situation, in der sie um einen so großen Betrag ansteigt, dass normale Verträge gebrochen werden, würde wahrscheinlich schnell mit einem Community Fork beantwortet werden. Aber ja, es gibt wahrscheinlich keine hervorragende Möglichkeit, dies auf evm-Ebene sicherzustellen