Wie kann ich die ursprüngliche Adresse eines Arrays mit dynamischer Größe im Speicher von Solidity zurückgeben?

Gibt im folgenden Quellcode memOffsetlokale Werte wie 96, 128 als Adresswert zurück. Wie könnte ich es dazu bringen, die ursprüngliche Adresse zurückzugeben, die sich im Speicher befindet, ist das möglich? Wird der Speicher nach dem Aufruf der Funktion f()wie lokale Variablen in C freigegeben?

Vollständiger Quellcode ist hier

contract C {
    function f(uint a, uint b) constant returns (uint[]) {
        assembly {
            // Create an dynamic sized array manually.
            let memOffset := mload(0x40) // 0x40 is the address where next free memory slot is stored in Solidity.
            mstore(memOffset, 0x20) // single dimensional array, data offset is 0x20
            mstore(add(memOffset, 32), 2) // Set size to 2
            mstore(add(memOffset, 64), a) // array[0] = a
            mstore(add(memOffset, 96), b) // array[1] = b
            return(memOffset, 128)
        }
    }
}

Antworten (1)

Überarbeitet, da die vorherige Antwort nicht ganz richtig war, da dies im Speicher möglich ist, aber nur in der Baugruppe durchgeführt werden kann und ziemlich kompliziert ist. Ich habe mir das in letzter Zeit ziemlich oft angesehen und Ihr Problem ist hauptsächlich auf einen Erinnerungszeiger zurückzuführen. Es gibt zwei Gründe, warum das obige im direkten Speicher nicht funktioniert; Der erste ist, dass der msize-Opcode auf den größten Speicherindex zeigen sollte, auf den zugegriffen wird, was jedoch nicht der Fall ist, der überschrieben wird. Zweitens müssen wir die Datentypgröße verwerfen, da diese automatisch von der EVM zugewiesen wird.

Wie Sie in Ihrem Beispiel sehen können, verwendet mload 0x40 (im Speicher wird dies auf die Bytes 64-96 verweisen), was die aktuell zugewiesene Speichergröße (msize) ist, und lässt die msize auf 0x20 (Byte 32) zeigen.

Layout im Speicher

Solidity reserviert drei 256-Bit-Slots:

0 - 64: Arbeitsspeicher für Hash-Methoden

64 - 96: aktuell zugewiesene Speichergröße (auch bekannt als freier Speicherzeiger)

Scratch Space kann zwischen Anweisungen verwendet werden (d. h. innerhalb der Inline-Assemblierung).

Solidity platziert neue Objekte immer am freien Speicherzeiger und Speicher wird nie freigegeben (dies könnte sich in Zukunft ändern).

Quelle: http://solidity.readthedocs.io/en/develop/miscellaneous.html#layout-in-memory

mGröße

"Speichergröße, dh größter zugegriffener Speicherindex"

Quelle: http://solidity.readthedocs.io/en/develop/assembly.html#opcodes

Aus dem Quellcode:

msize (beachten Sie, dass msize auch vom Lesezugriff auf den Speicher abhängt)

Quelle: https://github.com/ethereum/solidity/blob/18a72dbe4668e23aaf38404183b978fbbb1824d1/libevmasm/SemanticInformation.cpp#L63

Beispiel für einen Test, der die msize zurücksetzt:

(mstore 0x40 0)     ; Set initial MSIZE to 0x60

Quelle: https://github.com/ethereum/solidity/blob/6cbb726fb8970c6cb98e9b6a2928ef612ad9d760/test/libll/EndToEndTest.cpp#L641

Da der Code eine Rückgabe verwendet, hält er die Ausführung an und gibt den im Speicher gespeicherten Wert zurück, auf den verwiesen wird.

"Ausführung beenden, Datenspeicher zurückgeben[p..(p+s))"

Quelle: http://solidity.readthedocs.io/en/develop/assembly.html#opcodes

Dadurch wird der Zustand des Speichers eingefroren und der Wert des Speichers zurückgegeben, auf den Sie verweisen.

In Speicherlösung

Schauen wir uns an, wie wir einen fehlerhaften Wert zurückgeben, indem wir den Code folgendermaßen ändern:

contract C {

    function c() public returns (uint[]) {
        return f(1,2);
    }

    function f(uint a, uint b) private returns (uint[] memory memOffset) {
        assembly {
             // Create an dynamic sized array manually.
             // Don't need to define the data type here as the EVM will prefix it
             mstore(add(memOffset, 0x00), 2) // Set size to 2
             mstore(add(memOffset, 0x20), a) // array[0] = a
             mstore(add(memOffset, 0x40), b) // array[1] = b
        }
    }
}

Wir erhalten jetzt ein Array mit ungültigen Werten, was darauf zurückzuführen ist, dass der Vertrag zusätzliche Operationen ausführt, die den von Ihnen zugewiesenen Speicher überschreiben. Um dies zu beheben, müssen wir den Ort verschieben, an den wir den Speicher schreiben, und dem evm mitteilen, wohin msize zeigen soll.

contract C {

    function c() public returns (uint[]) {
        return f(1,2);
    }

    function f(uint a, uint b) private returns (uint[] memory memOffset) {
        assembly {
             // Create an dynamic sized array manually.
             // Don't need to define the data type here as the EVM will prefix it
             memOffset := msize() // Get the highest available block of memory
             mstore(add(memOffset, 0x00), 2) // Set size to 2
             mstore(add(memOffset, 0x20), a) // array[0] = a
             mstore(add(memOffset, 0x40), b) // array[1] = b
             mstore(0x40, add(memOffset, 0x60)) // Update the msize offset to be our memory reference plus the amount of bytes we're using
        }
    }
}

Das obige sollte nun den korrekten Wert zurückgeben.

Vorherige Antwort:

Dynamische Arrays können aufgrund ihrer nicht deterministischen Größe nicht im Arbeitsspeicher gespeichert werden. Wenn Sie ein dynamisches Array haben, das einen inkrementellen wachsenden und schrumpfenden Speicher hat, müssten Sie im Grunde entweder ständig im Speicher herummischen, das Risiko eingehen, vorhandenen Speicher zu überschreiben, oder einen großen Speicherblock reservieren, der unglaublich ineffizient und gaslastig ist. Andere Programmiersprachen haben dieses Problem mithilfe von Wörterbüchern und verknüpften Listenstrukturen gelöst, bei denen Sie auf verschiedene Speicherorte anstelle einer Speichersequenz verweisen. Der Grund, warum dies im Stapel/Speicher möglich ist, liegt darin, dass die Speicherung ähnlich wie bei einer verknüpften Liste optimiert wird, dies jedoch nicht auf die Verwendung im Speicher angewendet wurde. Kurz gesagt sind Arrays in Solidity noch lange nicht vollständig und benötigen noch etwas Arbeit, daher würde ich die Verwendung anderer Techniken wie das Speichern von Daten als Bytes in Betracht ziehen.