Einfache Funktion mit Inline-Assembly ändern

Ich versuche, einen supereinfachen Vertrag zu schreiben, der gaseffizient ist und Inline-Montage in Solidität verwendet. Alles, was diese Funktion tun muss, ist, einen intelligenten Kindervertrag mit einer Funktion zu erstellen, der Selbstzerstörung. Die Funktion sollte auch msg.sender überprüfen, um sicherzustellen, dass es sich um den Vertragseigentümer handelt. Dieses GasToken-Projekt tut dies in einer Form, in der sie das effektiv gespeicherte Gas tokenisiert haben. Sie brachten es auf eine andere Ebene, wo sie auch eine Vanity-Adresse verwendeten, um dies noch sparsamer zu machen. Ich habe versucht, eine Vanity-Adresse zu generieren, damit ich ihren Code genau verwenden kann, aber es dauert extrem lange, also habe ich darauf zurückgegriffen, den Code so zu ändern, dass ich es mit einer normalen Adresse und nicht mit einer Vanity-Adresse mit 10 machen kann führende Nullen. Lassen Sie uns für den Zweck dieser Frage also keine Eitelkeitsrede halten.

Vertrag Pseudocode:

if (msg.sender != 0x1234....5678) { throw; }
suicide(msg.sender)

Wie kann ich dies in Inline-Assembly in Solidität schreiben? Hier ist die Quelle für gasToken.io, wo sie die Vanity-Adresse verwendet haben . Ich möchte die Vanity-Adresse vorerst NICHT verwenden, um meinen Code zu vereinfachen, da diese Art von Vanity-Adresse ohne Mining-Hardware schwer zu generieren ist. Ich habe auf Solidity-Opcodes verwiesen , konnte dies jedoch nicht erfolgreich zum Laufen bringen.

// Creates a child contract that can only be destroyed by this contract.
function makeChild() internal returns (address addr) {
    assembly {
        // EVM assembler of runtime portion of child contract:
        //     ;; Pseudocode: if (msg.sender != 0x0000000000b3f879cb30fe243b4dfee438691c04) { throw; }
        //     ;;             suicide(msg.sender)
        //     PUSH15 0xb3f879cb30fe243b4dfee438691c04 ;; hardcoded address of this contract
        //     CALLER
        //     XOR
        //     PC
        //     JUMPI
        //     CALLER
        //     SELFDESTRUCT
        // Or in binary: 6eb3f879cb30fe243b4dfee438691c043318585733ff
        // Since the binary is so short (22 bytes), we can get away
        // with a very simple initcode:
        //     PUSH22 0x6eb3f879cb30fe243b4dfee438691c043318585733ff
        //     PUSH1 0
        //     MSTORE ;; at this point, memory locations mem[10] through
        //            ;; mem[31] contain the runtime portion of the child
        //            ;; contract. all that's left to do is to RETURN this
        //            ;; chunk of memory.
        //     PUSH1 22 ;; length
        //     PUSH1 10 ;; offset
        //     RETURN
        // Or in binary: 756eb3f879cb30fe243b4dfee438691c043318585733ff6000526016600af3
        // Almost done! All we have to do is put this short (31 bytes) blob into
        // memory and call CREATE with the appropriate offsets.
        let solidity_free_mem_ptr := mload(0x40)
        mstore(solidity_free_mem_ptr, 0x00756eb3f879cb30fe243b4dfee438691c043318585733ff6000526016600af3)
        addr := create(0, add(solidity_free_mem_ptr, 1), 31)
    }
}

Antworten (1)

Ich bin kein EVM-Assembler-Assistent, noch habe ich die Umgebung eingerichtet, um dies zu testen und für Sie zu debuggen - aber ich werde versuchen, einige Ratschläge / Ideen zu geben, wie ich dies tun würde. Die Kommentare in der Versammlung sind großartig, um uns durch das zu führen, was vor sich geht, also gehen wir sie durch.

Die Schritte, die ich in den Kommentaren sehe:

  • Erstellen Sie die Child-Laufzeit asm
  • Untergeordnetes asm in Bytes umwandeln
  • Schieben Sie untergeordnete ASM-Bytes in den Initcode für den untergeordneten Vertrag
  • Wandeln Sie den untergeordneten Initcode in einen Binärcode um
  • Legen Sie die Initcode-Binärdatei in den Speicher und rufen Sie create darauf auf

Gehen wir also die Schritte durch und sehen, was wir ändern müssen

Bauen Sie das Kind asm

Im ersten Teil (Definition des untergeordneten asm) sehe ich ein paar Dinge, die wir ändern müssen (meine hinzugefügten Kommentare in <>):

 // EVM assembler of runtime portion of child contract:
        //     ;; Pseudocode: if (msg.sender != 0x1234....5678) { throw; }
        //     ;;             suicide(msg.sender)
        <It was PUSH15 before because their vanity address only had 15 bytes of non0 data>
        <A normal ETH address has 20 bytes (40 hex characters)>
        //     <OLD>: PUSH15 0xb3f879cb30fe243b4dfee438691c04 ;; hardcoded address of this contract 
        //     <NEW>: PUSH20 0xb3f879cb30fe243b4dfee438691c04 ;; hardcoded address of this contract
        //     <... The rest of this seems like it should still work>

Untergeordnetes asm in Bytes umwandeln

Sie können diese Site verwenden, um den Bytecode für jeden Opcode abzurufen. Da wir nur den ersten Opcode von PUSH15 (6e) auf PUSH20 (73) und die Bytes danach geändert haben, sollten wir in der Lage sein, diese einfach auszutauschen.

ALT: 6eb3f879cb30fe243b4dfee438691c043318585733ff

NEU: 73<1234....5678>3318585733ff

Beachten Sie, dass Ihre neue untergeordnete Laufzeit-ASM statt 22 Byte 27 Byte groß sein sollte (5 mehr für Ihre längere Adresse).

Schieben Sie untergeordnete ASM-Bytes in den Initcode für den untergeordneten Vertrag

        //     PUSH27 0x73<1234....5678>3318585733ff
        //     PUSH1 0
        //     MSTORE ;; at this point, memory locations mem[10] through
        //            ;; mem[36] <added 5 bytes to the end here since your runtime is longer> contain the runtime portion of the child
        //            ;; contract. all that's left to do is to RETURN this
        //            ;; chunk of memory.
        //     PUSH1 27 ;; length <since your length is 27 not 22 now>
        //     PUSH1 10 ;; offset
        //     RETURN

Wandeln Sie den untergeordneten Initcode in einen Binärcode um

Ähnlich wie Schritt 2:

ALT: 756eb3f879cb30fe243b4dfee438691c043318585733ff6000526016600af3

7a) und ändern Sie PUSH1 22 zu PUSH1 27 (6016 zu 601b) NEU: 7a<73<1234....5678>3318585733ff>600052601b600af3

Jetzt sollten Sie in der Lage sein, dies in den ursprünglichen Code einzufügen mstore.

Endlich

Auch hier bin ich nicht sicher, ob dies funktioniert, und habe es nicht getestet. Aber hoffentlich hat es Ihnen geholfen, ein Gefühl dafür zu bekommen, wie Sie diese Art von Problem lösen können!

MSTORE sagt also "schreibt ein (u)int256 in den Speicher". Werde ich nicht die Größenbeschränkung für diesen einen einzelnen MSTORE-Aufruf erreichen, nachdem ich die Größe des Adressfelds von push22 auf push27 erhöht habe? Mein erster Versuch dazu scheiterte und das war meine beste Vermutung. Ich werde es basierend auf Ihrem Feedback noch einmal versuchen und wir werden sehen, ob ich auf dasselbe Problem stoße oder ob es einfach funktioniert! Wenn ich eine Art Fehler erhalte, der einen Konflikt mit diesem Größenproblem bestätigt, werde ich den Beitrag damit aktualisieren. Es ist auch möglich, dass ich die Funktionsweise von MSTORE falsch verstehe ...
Ein weiterer Verdächtiger hier, Irgendwelche Ideen, wie dieser Teil funktioniert: "PUSH1 10 ;; offset"? Ich kann nicht herausfinden, wie sie die 10 abgeleitet haben. Die Dokumentation sagt, dass MSTORE zwei Stack-Eingaben erwartet: "Offset" und "Wert". Und in ihrem Code nennen sie sie "Länge" und "Offset", und es ist ein bisschen verwirrend. Die Länge scheint offensichtlich, da stimme ich zu. Ich frage mich, ob eine Änderung der Länge auch den Versatz beeinflusst.
Offset bedeutet normalerweise, wo mit der Suche nach Bytes begonnen werden soll - daher denke ich nicht, dass die zusätzliche Länge des Laufzeitasms dies beeinflussen sollte. Ich bin mir jedoch nicht sicher, wie genau sie die 10 Bytes abgeleitet haben.
> Werde ich nicht die Größenbeschränkung für diesen einen einzigen MSTORE-Aufruf erreichen, nachdem ich die Größe des Adressfelds von push22 auf push27 erhöht habe? Ich glaube nicht, die Größe der untergeordneten Laufzeit ist insgesamt immer noch weniger als 256 Bit
Okay, ich habe es versucht und einen Kompilierungsfehler erhalten. Dies ist der Fehler, den ich ursprünglich bekam, als ich etwas Ähnliches versuchte: "TypeError: Number literal too large (> 256 bits) " und er zeigt auf die mstore-Zeile mit diesem großen Datengewirr. Das ist also über dem Limit für einen einzigen mstore-Aufruf. Vielleicht muss ich das in mehrere Mstore-Aufrufe aufteilen? Ich bin mir nicht sicher, wie ich das machen soll ... Hinweis: Ich erhalte diesen Compiler-Fehler nicht mit Solidity 0.4.11. Es funktioniert jedoch nicht (0x0-Transaktion abgebaut, aber Ausführung fehlgeschlagen). Wenn ich mit Solidity 0.5.7 kompiliere, gibt es diesen Fehler und wird nicht kompiliert.
Ich denke, der Grund, warum die gasToken.io-Entwickler eine Vanity-Adresse verwendet haben, liegt an diesem Limit. Dadurch können Sie einen einzigen mstore-Aufruf verwenden. Also muss ich entweder herausfinden, wie das mit mehreren mstore-Aufrufen geht, oder ich brauche Mining-Hardware, um eine Vanity-Adresse zu generieren.
Das macht Sinn. Ich glaube nicht, dass Sie eine vollwertige Mining-Hardware benötigen, um eine Vanity-Adresse mit den ersten 10 Zeichen zu generieren. github.com/johguse/profanity behauptet, dass es das in etwa 4 Stunden mit nur einer AMD-GPU erzeugen kann