Sollte ich Array-Elemente verschieben, wenn ich sie entferne?

Ich speichere ausstehende Operationen in einer Datenstruktur ähnlich der Multisig. Wallet- Vertrag , dh

struct PendingState {
        uint yetNeeded;
        uint ownersDone;
        uint index;
    }

und mein Vertrags-Pseudo-ish-Code:

contract c {
    mapping(bytes32 => PendingState) m_pending;
    bytes32[] m_pendingIndex;

    // initialize operation:
    var pending = m_pending[_h];
    pending.yetNeeded = bla;
    pending.ownersDone = bla;
    pending.index = m_pendingIndex.length++;
    m_pendingIndex[pending.index] = _h;

    // remove the current operation when finished
    delete m_pendingIndex[m_pending[_h].index];
    delete m_pending[_h];
}

Wenn es jedoch drei (Index = 0, 1, 2) ausstehende Operationen gab, als wir die zweite abgeschlossen und entfernt haben, dann sind wir am Ende gefüllt und m_pendingIndex[0]leer . Erhöht sich dadurch der Gasbedarf für spätere Operationen? Soll ich die Objekte zurückschieben, bis keine freie Stelle mehr vorhanden ist? Ich kann mir vorstellen, dass dieser Prozess selbst Kosten verursacht, aber er könnte vorzuziehen sein, um DOS-Angriffe zu verhindern.m_pendingIndex[2]m_pendingIndex[1]

Danke für jede Hilfe,

Antworten (3)

Diese Zeilen

delete m_pendingIndex[m_pending[_h].index];
delete m_pending[_h];

tun Sie nicht, was Sie wahrscheinlich denken, dass sie tun. Es genügt zu sagen, dass Sie keine Informationen aus einer unveränderlichen Blockchain entfernen können.

Wie Edmund sagte, verknüpfte Listen. Es gibt eine Möglichkeit, ein Element effizient aus einem ungeordneten Array zu entfernen (damit es nicht unbegrenzt wächst) und dies mit begrenzten Gaskosten. Die Löschoperation ist also in jeder Größenordnung gleich teuer. In Summe:

Geben Sie hier die Bildbeschreibung ein

Ich habe eine Kombination von Zuordnungen mit Zeigern auf Array-Zeilen verwendet, um ein logisches Löschen aus Arrays und eine Reihe anderer Vorteile zu ermöglichen. Funktioniert in vielen Situationen:

Geben Sie hier die Bildbeschreibung ein

Ausführlichere Beschreibung: https://medium.com/@robhitchens/solidity-crud-part-1-824ffa69509a#.c3e5nom01

Möglicherweise kann das Muster in dieser Situation helfen.

Fällt diese Lösung nicht aus, wenn nur ein Element in der Liste vorhanden ist? Oder würden Sie in diesem Fall nur den lengthRücken nach unten stellen 0?
Nein. Wenn es 1 Element in der Liste gibt, zeigt der Zeiger 0 (Zeile 0) und die Länge ist eins.
Richtig, aber wenn Sie dieses einzelne Element "löschen" möchten, können Sie es nicht durch das letzte Element in der Liste ersetzen, da es selbst das letzte Element ist.
Ich verstehe, worauf Sie hinauswollen. Der Referenzcode funktioniert. rowToDelete und rowToMove sind gleich, also macht es eine Art sinnlosen Vorgang, behält aber das Schöne und Einfache bei. Die Indexlänge geht von 1 auf 0 (wie üblich) und das letzte Element wird entfernt.
Danke für die Erklärung, das macht Sinn. Eine letzte Frage. Wenn Sie einen Benutzer aus der userStructsZuordnung "löschen", setzen Sie ihn nie userStructs[userAddress].indexauf seinen Standardzustand zurück (der verwirrenderweise 0) ist. Es scheint keine Rolle zu spielen, da isUser()überprüft wird, ob der Benutzer an diesem bestimmten Index derselbe ist wie der, nach dem Sie suchen, aber es ist schlecht, wenn ein falscher Index in einem "deleted" hängt UserStruct?
Keine bekannten Probleme mit dem Muster; bitte melden wenn ihr was findet! delete userStructs[userAddress]:-) Sie können (angepasst an Ihren Anwendungsfall) überlegen , aber die Logik des Systems benötigt es nicht . Tatsächliche Löschungen können dazu beitragen, die Größe des Zustandsversuchs zu reduzieren, was dazu beitragen kann, die Synchronisierung für einige Clients zu optimieren. Es gibt negative Gaskosten für diese OP (Rückerstattung), aber in der Praxis habe ich festgestellt, dass die Gesamttransaktionskosten für deletegewöhnlich steigen, sonst wäre es Teil des Musters.

Wenn Sie alles hochschalten müssen, haben Sie möglicherweise unbegrenzte Benzinkosten, was Sie im Allgemeinen vermeiden möchten, es sei denn, Sie wissen, dass es eine praktische Grenze gibt. Aber wenn Sie die Elemente nicht entfernen, haben Sie stattdessen möglicherweise endlos wachsende Kosten, wenn Sie stattdessen die Daten durchlaufen müssen, da Sie alle fehlenden Einträge lesen und feststellen müssen, dass sie nicht vorhanden sind.

Die Lösung besteht oft darin, anstelle eines Arrays eine verkettete Liste (oder manchmal eine doppelt verkettete Liste) zusammen mit einer Variablen zu verwenden, die Ihnen sagt, welches Element das erste in der Liste ist. Auf diese Weise können Sie Artikel mit vorhersehbaren, begrenzten Benzinkosten aus der Liste entfernen, unabhängig von der Anzahl der Artikel in der Liste.

Ich würde empfehlen, zuerst den zu löschenden Index und den letzten Index auszutauschen und dann einfach 'pop()' zu verwenden.