Effiziente Methode zum Überprüfen und Einfügen von eindeutigen Array-Adressen

Ich habe eine folgende Anforderung, die in DAPP erfüllt werden muss.

  1. Array von Adressen einfügen
  2. Das Array muss eindeutig sein. Wenn eine der Array-Adressen bereits vorhanden ist, lehnen Sie den gesamten Vorgang ab und machen Sie ihn rückgängig .
  3. dapp muss in der Lage sein, die Benutzeradressliste abzurufen
  4. dapp muss in der Lage sein, anhand einer Benutzeradresseneingabe zu erkennen, ob eine Benutzeradresse existiert

Notiz:

  1. Die obige Anforderung muss erfüllt sein.
  2. Die vollständige Validierungsprüfung muss per Smart Contract erfolgen (kann sich nicht allein auf die clientseitige Prüfung verlassen).

Der Versuch, eine 3 for-Schleife zu verwenden, scheint dafür kein sehr cleverer Weg zu sein, da dies zu einer kostenintensiven Operation wird. Leider ist dies die einzige Lösung, die mir einfällt, um die obige Bedingung zu erfüllen. Hat jemand Erfahrung damit umzugehen?

// probably only one of mapping or list is needed
address[] public addressList;
mapping (address => bool) public userAddr; 


    function insertAddress(address[] addressUser) public returns (bool) {
        // loop addressUser and check if it is unique
        // loop addressUser and check if it exists in mapping
        // loop and push addressUser to addressList + insert addressUser to userAddr


        return true;
    }

Antworten (2)

Wenn ich Ihre Anforderung sehe, würde ich Folgendes tun:

address[] public addressList;
mapping (address => bool) public userAddr; 

function insertAddress(address[] addressUser) public returns (bool) {
    // used to check adressUser uniqueness
    mapping (address => bool) memory uniq;
    for(uint i = 0; i < addressUser.length, i++) {
        address addr = addressUser[i];
        // check if addressUser is unique
        require(uniq[addr] == false);
        uniq[addr] = true;
        // if addr is not already list
        if (userAddr[addr] == false) {
            userAddr[addr] = true;
            addressList.push(addr);
        }
    }

    return true;
}

Bearbeiten:

Nachdem Sie einen anderen Vertrag gesehen haben, können Sie die Methode hier verwenden und verlangen, dass alle Adressen in aufsteigender Reihenfolge gesendet werden. Es ist wahrscheinlich weniger kostspielig in Bezug auf Benzin, da weniger Speicherzuweisung.

Das würde gehen:

function insertAddress(address[] addressUser) public returns (bool) {
    // used to check adressUser uniqueness
    address lastAddr = address(0);
    for(uint i = 0; i < addressUser.length, i++) {
        address addr = addressUser[i];
        // check if addressUser is unique
        // by forcing all address to be sent 
        // in increasing order
        require(addr > lastAddr);
        lastAddr = addr;
        // if addr is not already in list
        if (userAddr[addr] == false) {
            userAddr[addr] = true;
            addressList.push(addr);
        }
    }

    return true;
}

 

Ich habe solche Verträge schon früher erstellt und viele der Probleme gesehen, die Sie haben. Kurz gesagt, diese Operationen, die Sie durchzuführen versuchen, sind teuer . Der beste Weg, dies zu tun, ist es einfach, es effizient zu tun.

Um die Anforderungen zu erfüllen, würde ich die gesamte Funktion in eine Schleife stecken, die über jede übergebene Adresse iteriert. Um es so effizient wie möglich zu machen, können Sie mit einer ifAnweisung prüfen, ob die Adresse bereits existiert. Wenn dies der Fall ist, können Sie einfach zur nächsten Adresse wechseln, ohne weitere Berechnungen an der aktuellen Adresse durchzuführen . Je nachdem, wie Sie Adressen übergeben, kann dies zu enormen Spriteinsparungen führen.

Als Beispiel könnten Sie Folgendes tun:

address[] public addressList;
mapping (address => bool) public userAddr; 


    function insertAddress(address[] addressUser) public returns (bool) 
    {
        for (uint256 i = 0; i < addressUser.length; i++) {
            if (address does not exist) {
                push addressUser to addressList
                insert addressUser to userAddr
            }
        }

        return true;
    }

Bearbeiten Sie basierend auf den neuen Anforderungen

Das Array muss eindeutig sein. Wenn eine der Array-Adressen bereits vorhanden ist, lehnen Sie den gesamten Vorgang ab und setzen Sie ihn zurück.

Wenn dies der Fall ist, trifft das oben Genannte nicht zu und was Sie ursprünglich hatten, ist der beste Weg, dies zu tun.

Bearbeiten Sie basierend auf Kommentaren

Wenn Sie logisch darüber nachdenken, müssen Sie jede Adresse überprüfen, um das Array zu überprüfen. Dies erfordert Opcodes, die jedes Element überprüfen, und Sie können dies nicht umgehen. Nachdem diese Überprüfungen durchgeführt wurden, müssen Sie jedes Element in die Blockchain schreiben, was auch Opcodes für jedes dieser Elemente erfordert. Dieser gesamte Prozess ist rechenintensiv und ist der Grund, warum ein Großteil dieser Logik im Allgemeinen außerhalb der Kette vorgeschlagen wird.

Eine Sache, die Sie tun können, ist, die Hashes der Arrays zu vergleichen (eines, das Sie übermitteln, und eines, das überprüft wird). Sie können die keccack256von jedem Array nehmen, um einen eindeutigen Hash sicherzustellen. Sie können diesen Hash im Smart Contract speichern und anstelle des gesamten Arrays einen zu prüfenden Hash senden. Auf diese Weise führen Sie jetzt nur eine Prüfung durch, im Gegensatz zu N Prüfungen (N ist die Anzahl der Elemente im Array).

Sie müssen immer noch eine Schleife ausführen, um alle Artikel zum Smart Contract hinzuzufügen, aber jetzt haben Sie effektiv eine Schleife entfernt .

Ein Beispiel wäre:

address[] public addressList;
mapping (address => bool) public userAddr;
mapping (bytes32 => bool) public doesHashExist; 


    function insertAddress(address[] addressUser) public returns (bool) 
    {
        bytes32 newHash = keccak256(addressUser);
        require(!doesHashExist[newHash]);
        for (uint256 i = 0; i < addressUser.length; i++) {
                push addressUser to addressList
                insert addressUser to userAddr
            }
        }
        doesHashExist[newHash] = True;
        return true;
    }

Sie werden sehen, dass es eine neue Zuordnung gibt, die zum Speichern von Hashes verwendet wird, die im Vertrag vorhanden sind. Die Funktion führt jetzt nur eine einzige Prüfung (sowie einen Hashing) durch, um die Existenz des Arrays zu bestätigen. Schließlich speichert es den Hash.

Das obige u-Array besteht darin, nicht eindeutige Adressen zu überspringen und einzufügen, von denen ich gesehen habe, dass sie häufig geübt werden. aber die Anforderung für die dapp ist, die gesamte Operation abzulehnen, selbst wenn eine davon einzigartig ist.
Ich verstehe. Wenn das der Fall ist, dann ist dies eine sehr einfache Funktion und die von Ihnen beschriebene funktioniert. Die Funktion prüft einfach, ob Adressen eindeutig sind, und fügt sie andernfalls dem Array hinzu.
Ja, ich weiß, dass es funktioniert, aber es erfordert mindestens 3 for-Schleife (eine zum Überprüfen, ob der Array-Pass an sich eindeutig ist, eine zum Überprüfen, ob der Array-Pass einen vorhandenen Wert hat, eine zum Einfügen). Ich versuche, nach einem kostengünstigeren Weg zu suchen.
Der effizienteste Weg wäre, den Check Off-Chain auf der Dapp-Schicht durchzuführen. Sie können sehr einfach überprüfen, ob ein Array eindeutig ist, ohne etwas in die Blockchain zu schreiben. Wenn es eindeutig ist, können Sie dann eine Transaktion an das Netzwerk senden und müssen keine der in Solidity ausführen.
Die Anforderung muss gemäß der Anforderung im Smart Contract selbst erfüllt werden. Es sollte nicht auf externe Validierung angewiesen sein. Gibt es eine Möglichkeit, 3 für Schleifen auf 2 zu reduzieren? (z. B.: for-Schleife zum Überprüfen des Arrays selbst, ob es eindeutig ist, kann auf andere effizientere Weise überprüft werden?)
Ich habe die Antwort oben aktualisiert.
Warum sollte es vorgeschlagen werden, Offchain zu tun? Die Offchain-Validierung kann einfach umgangen werden, indem die Smart-Contract-Funktion direkt von der Benutzer-Wallet-Adresse aufgerufen wird
Mein neuer Vorschlag mit Hashes wendet diese Bedenken ab.
You can take the keccak256 of each array in order to ensure a unique hash.Ich habe nicht wirklich verstanden, wie dies eine For-Schleife reduzieren wird. keccak256(array[] addressList1) === keccak256(array[] addressList2)kann nur prüfen, ob zwei Array-Listen gleich sind (in Bezug auf Länge, Wert und Reihenfolge der Werte). Es kann nicht identifiziert werden, ob ein Element eines Arrays in einem anderen Array vorhanden ist, oder ich missverstehe die Implementierung.
Sie würden einen Hash übermitteln und ihn mit einer vorhandenen Zuordnung vergleichen. Ich werde der Antwort Code hinzufügen.