Warum verwendet DELEGATECALL 94k Gas in meinem Vertrag in Remix?

Ich wurde von einer anderen Frage inspiriert , mehr über Stapeltiefenangriffe zu erfahren.

Wenn ich versuche, attack(1023)Remix für diesen Vertrag auszuführen:

pragma solidity ^0.4.14;

contract Attacker {
    uint x;

    function attack(uint y) {
        if (y > x) {
            this.delegatecall(bytes4(sha3('attack(uint256)')), --y);
        }
        else {
            throw ; // doesn't matter, we never get here
        }
    }
}

Mit 3M-Gas geht mir das Benzin 228 Anrufe tief aus. Gegen Ende scheinen die DELEGATECALL-Anweisungen etwa 1,5.000 Gas zu verbrauchen. Aber beim ersten DELEGATECALL sehe ich 94.000. Hier sind die Schritte, die ich verwende, um diese Nummer zu generieren:

  1. Erstellen Sie den obigen Vertrag in Remix mit Javascript VM
  2. Anrufattack(1023)
  3. Klicken Sie in den Debugger, nachdem ihm das Gas ausgegangen ist
  4. Gehen Sie mit geöffneter "Anweisungen"-Leiste bis kurz vor DELEGATECALL durch
  5. Unter "Schrittdetail" notieren Sie "Restgas" von 5977987
  6. Klicken Sie auf „Vorwärts gehen“
  7. Unter "Schrittdetail" notieren Sie "Restgas" von 5883892

Antworten (1)

Aktualisieren. Ich lasse die Antwort unten stehen, da sie einige interessante Punkte enthält, aber ich glaube, ich bin dem endlich auf den Grund gegangen.

Der Schlüsselpunkt ist auf der Seite Feinheiten vergraben :

Ein CALL oder CREATE kann höchstens 63/64 des zum Zeitpunkt des CALL verbleibenden Gases verbrauchen; wenn ein CALL nach mehr als diesem vorgeschriebenen Maximum verlangt, dann wird der innere Ruf nur das vorgeschriebene maximale Gas haben, unabhängig davon, wie viel Gas verlangt wurde.

Dies wird im YP ziemlich klar erklärt , also weiß ich nicht, wie ich es verpasst habe :-)Geben Sie hier die Bildbeschreibung ein

Ihr CALL verbraucht also nicht so viel Gas, sondern Sie sind nicht in der Lage, das gesamte Gas, das Sie möchten, an den nächsten CALL in der Rekursion weiterzugeben. Wenn Ihr anfängliches Gas bei etwa 6,4 Mio. liegt, wird Ihr erster CALLed-Vertrag etwa 100.000 Gas weniger erhalten, als Sie dachten. Ich denke, das ist, was Sie sehen. Das überschüssige Gas, das nicht an den CALL weitergegeben wird, bleibt bei Vertragsbeendigung ungenutzt, es wird nicht verbraucht.

Also - kein Remix-Glitch (sorry, Remix!). Wenn überhaupt, sieht es so aus, als ob testrpc dies nicht erzwingt.


Ich denke, dies ist ein Berichtsproblem mit Remix. Ich habe eine geringfügige Variation Ihres Vertrags gegen testrpc (dh nicht Remix - in meiner eigenen Umgebung) ausgeführt und sehe nicht dasselbe.

pragma solidity ^0.4.13;

contract Attacker {
    uint256 x;

    function attack(uint256 y) payable returns(uint256) {
        if (y > 0) {
            this.delegatecall(bytes4(sha3('attack(uint256)')), --y);
        }
        else {
             // Save the remaining gas in storage so that we can access it later
             x = msg.gas;
             return;             
        }
    }
}

Das Start-y ist dann die Anzahl der delegatecallVerwendungen. Die verbleibende Gasmenge bleibt im Speicher, auf den mit zugegriffen werden kann eth_getStorageAt.

Ergebnisse (y ist die Anzahl der Delegiertenanrufe, der Wert ist das verbleibende Gas, diff ist die Differenz des Gases zwischen diesem und dem vorherigen).

  • y=0, 2978464
  • y=1, 2977288, diff = 1176
  • y=2, 2976176, diff = 1112
  • y=3, 2975064, diff = 1112
  • y=4, 2973952, diff = 1112

In jedem Fall beträgt die Differenz 1112 Gas, einschließlich der Differenz zwischen null Delegiertenanrufen und einem Delegiertenanruf (mit Ausnahme von zusätzlichen 64 Gasen, die meiner Meinung nach auf das zusätzliche Nullbyte in den anfänglichen Anrufdaten zurückzuführen sind, wenn y = 0; dies wird mit 4 Gas berechnet statt 68 für ein Nicht-Null-Byte).

Das ist im Wesentlichen das, was ich erwarten würde, weshalb ich denke, dass Remix hier etwas wackelig sein muss.

Bearbeiten: Nur um die Remix-Schwachheit zu bestätigen. Mit demselben Programm: von der internen Gasmessung ( msg.gaseingelagert) berichtet Remix, dass jedes zusätzliche Mal rund um den Delegiertenruf weitere etwa 45000 Gas verbraucht wird; Die von Remix auf der Registerkarte „Vertrag“ gemeldeten Vertragsausführungskosten steigen jedoch nur um 1339 Gas pro Delegiertenanruf.

Beachten Sie, dass das Gas vor dem sstorevon berechnet wird x, sodass wir die (großen) Kosten dafür nicht sehen. Aber das macht nichts, da es sich um eine einmalige Sache handelt.

Großartig, ich habe ein Issue auf Remix eröffnet: github.com/ethereum/remix/issues/539
@carver - Ich glaube, ich bin dem auf den Grund gegangen. Bitte sehen Sie sich das Update an, das ich an der Antwort vorgenommen habe. Ganz subtil. Ich denke jetzt, dass Remix darüber im Klaren ist. Entschuldigung für die anfängliche Fehlleitung.
Subtil, in der Tat. Das erklärt auch, warum sich die Summen für „verbrauchtes Gas“ von den Berichten für „verbleibendes Gas“ auf der Registerkarte „Debugger“ unterscheiden. Ich habe das Thema auf Remix geschlossen. Wow, dieser gelbe Papierabschnitt ist stumpf. Ich habe eine Weile gebraucht, aber die relevante Definition darüber gefunden: L(n) = n - floor(n/64). Dieses Problem wurde stattdessen auf testrpc geöffnet: github.com/ethereumjs/testrpc/issues/367 @benjaminion Können Sie mir Ihre testrpc-Version/-Umgebung mitteilen, die ich der Problembeschreibung hinzufügen soll?
@carver - Ich habe Versionsinformationen zu Ihrem Bericht für testrpc hinzugefügt; Sie sind ein guter Bürger! Soweit ich das beurteilen kann, wurde dieses Verhalten in der EIP150-Fork genau eingeführt, um Stack-Depth-Angriffen entgegenzuwirken.