Solidity: Wie kann ich delete anwenden, um Speicher ref[] mit einem Aufruf abzuschließen?

Definition:

 mapping(address => data)         clusterContract;

 struct data { //defined inside a Library.
    mapping(address => Library.Struct[]) my_status;
 }
 data list;     
 clusterContract[id] = list;

Verwendung:

clusterContract[msg.sender].my_status[id].push( Library.Struct({ status: status_ }));
delete clusterContract[msg.sender].my_status; //<=error occurs.

Fehler tritt auf:

Error: Unary operator delete cannot be applied to type 
mapping(address => struct ReceiptLib.Status storage ref[] storage ref)
    E               delete clusterContract[msg.sender].my_status;

Andererseits: delete clusterContract[msg.sender].my_status[id]funktioniert.

=> Vervollständigt delete clusterContract[msg.sender]removes(initialize to 0) my_statusauch das Array von?

[F] Wie kann ich mich deletefür den vollständigen Speicher ref[] bewerben? oder muss ich ref [] (die ids verfolgen) iterieren und lösche einzeln anwenden, wie im beispiel gezeigt?

Beispiel:

for(int i=0;i<storedIDs.size();i++)
   delete clusterContract[msg.sender].my_status[storedIDs[i]];

Vielen Dank für Ihre wertvolle Zeit und Hilfe.

Antworten (2)

Das Löschen von Daten gibt Zustandsraum auf Clients frei State Tree Pruningund sollte nach Möglichkeit durchgeführt werden, wenn sie nicht mehr benötigt werden.

Zuordnungen können jedoch nicht einfach in einem einzigen Vorgang gelöscht werden, da der Vertrag selbst nicht weiß, wo sich die Daten in der staatlichen Adresse befinden. Dazu muss der Schlüssel bereitgestellt werden, um die einzeln zugeordneten Elemente zu löschen.

Unbegrenzte Arrays wie uint[] bytesusw. können in einem solidityVorgang gelöscht werden, jedoch muss die EVM iterativ jeden Steckplatz einzeln löschen. Es muss darauf geachtet werden, dass solche Arrays nicht zu groß werden, da ein zukünftiger Massenlöschvorgang das Blockgaslimit erschöpfen und Ihren Vertrag brechen könnte. Die Lektion hier ist, dass Ihre Garbage Collection auch atomar sein sollte.

Das Speichern neuer Daten kostet 20.000 pro Slot, während das Überschreiben von Daten nur 5.000 kostet. Das Löschen von Daten kostet ebenfalls 5.000 pro Slot, bringt aber am Ende Ihres Anrufs eine Rückerstattung von 15.000 Benzin pro Slot.

Was Rob vorgeschlagen hat, ist eine gute Praxis mit Arrays, da Sie neu initialisierten Speicher einfach wiederverwenden können, indem Sie ihn lengthauf 0 setzen. Sie verwenden jedoch keine Arrays, sondern Zuordnungen, was ein ganz anderes Ballspiel ist.

Also, von Ihren Codefragmenten ...

struct data {
    //defined inside a Library.
    mapping(address => Library.Struct[]) my_status;
}

mapping(address => data) clusterContract;
data list;     
clusterContract[id] = list;

...

clusterContract[msg.sender].my_status[id].push( Library.Struct({ status: status_ }));

wenn du es versuchst:

delete clusterContract[msg.sender].my_status; //<=error occurs.

Sie versuchen, ein gesamtes Mapping auf einmal zu löschen, weil .mystatuses wie folgt definiert ist:

mapping(address => Library.Struct[]) my_status;

aber ein Mapping selbst kann nicht gelöscht werden, nur seine Elemente.

Insbesondere wenn ich mit Mappings von Strukturen arbeite, schreibe ich Dekonstruktoren, um sie zu bereinigen. Also sowas wie:

function destroy(id) internal

{
   delete clusterContract[msg.sender].my_status[storedIDs[id]]; 
}

Versuchen Sie jedoch erneut, auf atomare Operationen mit bekannten Lebenszyklen hinzuarbeiten, anstatt sich auf teure und unordentliche Massenbereinigungen verlassen zu müssen.

Löschen macht nicht viel mit den zugrunde liegenden Daten, da es sich um ein reines Anhänge-Speichersystem handelt. Egal was wir tun, die Daten sind immer noch in einem früheren Weltzustand vorhanden.

Da Sie alle Elemente im Array auf einmal entfernen möchten:

pragma solidity ^0.4.6;

contract NullifyArray {

    address[] public status;

    function pushArray(address data)
        public
        returns(uint arrayLength)
    {
        return status.push(data);
    }

    function getArrayLength() 
        public 
        constant
        returns (uint arrayLength)
    {
        return status.length;
    }

    function nullifyArray()
        public
        returns(uint arrayLength)
    {
        status.length=0;
        return status.length;
    }

}

Dadurch bleibt die Grundstruktur bestehen, dh die Struktur hat immer noch ein Array, aber seine Länge ist 0 und der öffentliche Getter kann auf keine Elemente zugreifen. Es hat nichts dazu beigetragen, die Daten zu löschen, die in der Kette vorhanden sind, aber sie sind logischerweise verschwunden.

Wenn Sie wählerisch sein müssen, welche Array-Zeilen entfernt werden sollen, schlage ich hier das Muster Mapped Structs with Delete-enabled Index vor: Gibt es gut gelöste und einfache Speichermuster für Solidity?

Ich hoffe es hilft.

Smart und diese Lösung verbraucht nur wenig Gas. Daten werden also nie gelöscht, aber logischerweise können wir Bedingungen stellen, damit Kunden nicht auf die Daten zugreifen können. Wenn ich zum Beispiel getArrayValue() bekomme, wenn status.length 0 ist (auch wenn es vor dem Löschen 100 war), kann ich das throwfür den Benutzer tun, der auf die Daten zugreifen möchte? Die Sache ist, dass es einen Löschvorgang geben könnte und über dieselbe ID neu erstellt werden kann. @Rob Hitchens
Bitte beachten Sie, dass ich nach einer Lösung suche, um Array vollständig zu entfernen. Aber auf jeden Fall werde ich mir den Link ansehen, den Sie geteilt haben. @Rob Hitchens
Auf der Rückleseseite, wenn array.length==0 und eine Funktion versucht, auf array[0] zuzugreifen, wird die EVM für Sie auslösen.
Also im Grunde status.length = 0den Job machen.
Yip. Und das zu vorhersehbaren und konsistenten Gaskosten in jeder Größenordnung.
Aber es gibt ein Problem. In meinem Fall muss ich Ihre Lösung auf alle my_status-Arrays anwenden my_status[id].length. Da ich die Schlüssel nicht kenne, muss ich sie auch speichern, um wieder auf sie zugreifen zu können, um ihre Länge auf 0 zu initialisieren. @Rob Hitchens
Nein. Kein Problem, oder nicht wie angegeben. IMO, das Problem ist nicht, was zu tun ist, wenn die Schlüssel nicht bekannt sind. Wenn die Daten nicht so organisiert sind, dass sie den Anwendungsfall unterstützen, wird das Leben in der Tat sehr schwierig. Sie möchten alle Status-Arrays für alle IDs löschen? Dies kann bei entsprechender Datenstruktur in einem Zug erfolgen. Lassen Sie mich wissen, ob ich das richtig verstehe, denn es klingt nach einer interessanten Herausforderung.
Ja, Herr, Sie haben es richtig verstanden. Ich möchte alle Status-Arrays für alle zuvor erstellten IDs löschen. @Rob Hitchens
Ich sehe keinen offensichtlichen Weg, die Beziehungen aufrechtzuerhalten und auch alles in einem Zug auszulöschen. Was Sie tun können, ist sicherzustellen, dass Sie die Vertragsadressen iterieren können. Führen Sie eine Liste. Anstatt zu versuchen, innerhalb des Vertrags zu iterieren, verwenden Sie einen vertrauenswürdigen Client, um die IDs zu iterieren und dabei alle verknüpften Status-Arrays zu zerstören. Dadurch bleiben die Benzinkosten pro Transaktion konstant ... Sie haben nur viele Transaktionen im "Job". Im Idealfall sollte eine groß angelegte Reorganisation möglichst vermieden werden.
Es muss nicht in einem Zug sein. Danke, ich habe die Hauptidee @Rob Hitchens
@Rob Hitchens Das Löschen von Daten gibt Speicherplatz auf Clients frei State Tree Pruningund sollte nach Möglichkeit durchgeführt werden, um Speicherplatz zurückzugewinnen.
@o0ragman0o Danke dafür! Wo finde ich eine ELI5-Beschreibung des aktuellen Stands der Implementierung? Ich habe dies unter Bezugnahme auf den Artikel von VB aus dem Jahr 2015 gefunden. Ich bin mir etwas unsicher, was im aktuellen Protokoll enthalten ist. ethereum.stackexchange.com/questions/174/…
@Rob Hitchens Ich kenne mich selbst nicht und habe nicht viel darüber veröffentlicht, also denke ich, dass es etwas sein könnte, bei dem Kunden sich in ihrer Implementierung unterscheiden können.