Gibt es gut gelöste und einfache Speichermuster für Solidity?

Eine einfache und angemessene Datenorganisation kann Solidity-Neulinge herausfordern. Es möchte, dass wir alles auf eine Weise organisieren, an die viele von uns nicht gewöhnt sind.

Gibt es gut gelöste allgemeine Muster für die routinemäßige On-Chain-Datenorganisation?

welche Art von Speicher? Ich stelle fest, dass es in den Beispielen noch keine sortierte Speicherung gibt.
Solidity CRUD Library (2019) implementiert Mapped Structs mit Delete: medium.com/robhitchens/solidity-crud-epilogue-e563e794fde

Antworten (4)

Hier sind einige einfache und nützliche Muster in aufsteigender Reihenfolge der Nützlichkeit.

Ereignisprotokolle werden der Kürze halber weggelassen. In der Praxis ist es wünschenswert, Ereignisse für jede wichtige Zustandsänderung auszugeben.

Einfache Liste mit Array

Stärken

  • Zuverlässig chronologische Reihenfolge
  • Liefert eine Zählung
  • Zufälliger Zugriff nach Zeilennummer (nicht ID)

Schwächen

  • Kein wahlfreier Zugriff per ID
  • Keine Zusicherung der Einzigartigkeit
  • Keine Prüfung auf Duplikate
  • Wildes Wachstum der Liste

Beispiel:

pragma solidity ^0.4.6;

contract simpleList {

  struct EntityStruct {
    address entityAddress;
    uint entityData;
    // more fields
  }

  EntityStruct[] public entityStructs;

  function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
    EntityStruct memory newEntity;
    newEntity.entityAddress = entityAddress;
    newEntity.entityData    = entityData;
    return entityStructs.push(newEntity)-1;
  }

  function getEntityCount() public constant returns(uint entityCount) {
    return entityStructs.length;
  }
}

Zuordnung mit Struct

Stärken

  • Zufälliger Zugriff durch eindeutige ID
  • Zusicherung der Eindeutigkeit der ID
  • Schließen Sie Arrays, Zuordnungen und Strukturen in jeden "Datensatz" ein

Schwächen

  • Kann die Schlüssel nicht aufzählen
  • Kann die Schlüssel nicht zählen
  • Benötigt eine manuelle Überprüfung, um einen Standardwert von einem expliziten "alle 0"-Datensatz zu unterscheiden

Beispiel:

contract mappingWithStruct {

  struct EntityStruct {
    uint entityData;
    bool isEntity;
  }

  mapping (address => EntityStruct) public entityStructs;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
    return entityStructs[entityAddress].isEntity;
  }

  function newEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(isEntity(entityAddress)) revert(); 
    entityStructs[entityAddress].entityData = entityData;
    entityStructs[entityAddress].isEntity = true;
    return true;
  }

  function deleteEntity(address entityAddress) public returns(bool success) {
    if(!isEntity(entityAddress)) revert();
    entityStructs[entityAddress].isEntity = false;
    return true;
  }

  function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) revert();
    entityStructs[entityAddress].entityData = entityData;
    return true;
  }
}

Array von Strukturen mit eindeutigen IDs

Stärken

  • Zufälliger Zugriff nach Zeilennummer
  • Zusicherung der Eindeutigkeit der ID
  • Arrays, Mappings und Structs mit jedem "Record" einschließen

Schwächen

  • Kein wahlfreier Zugriff per ID
  • Wildes Wachstum der Liste

Beispiel:

contract arrayWithUniqueIds {

  struct EntityStruct {
    address entityAddress;
    uint entityData;
  }

  EntityStruct[] public entityStructs;
  mapping(address => bool) knownEntity;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
    return knownEntity[entityAddress];
  }

  function getEntityCount() public constant returns(uint entityCount) {
    return entityStructs.length;
  }

  function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
    if(isEntity(entityAddress)) revert();
    EntityStruct memory newEntity;
    newEntity.entityAddress = entityAddress;
    newEntity.entityData = entityData;
    knownEntity[entityAddress] = true;
    return entityStructs.push(newEntity) - 1;
  }

  function updateEntity(uint rowNumber, address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) revert();
    if(entityStructs[rowNumber].entityAddress != entityAddress) revert();
    entityStructs[rowNumber].entityData    = entityData;
    return true;
  }
}

Zugeordnete Strukturen mit Index

Stärken

  • Wahlfreier Zugriff durch eindeutige ID oder Zeilennummer
  • Zusicherung der Eindeutigkeit der ID
  • Schließen Sie Arrays, Zuordnungen und Strukturen in jeden "Datensatz" ein
  • Die Liste behält die Reihenfolge der Deklaration bei
  • Zähle die Rekorde
  • Zählen Sie die IDs auf
  • Löschen Sie ein Element "sanft", indem Sie einen booleschen Wert setzen

Schwächen

  • Wildes Wachstum der Liste

Beispiel:

contract MappedStructsWithIndex {

  struct EntityStruct {
    uint entityData;
    bool isEntity;
  }

  mapping(address => EntityStruct) public entityStructs;
  address[] public entityList;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
      return entityStructs[entityAddress].isEntity;
  }
  
  function getEntityCount() public constant returns(uint entityCount) {
    return entityList.length;
  }

  function newEntity(address entityAddress, uint entityData) public returns(uint rowNumber) {
    if(isEntity(entityAddress)) revert();
    entityStructs[entityAddress].entityData = entityData;
    entityStructs[entityAddress].isEntity = true;
    return entityList.push(entityAddress) - 1;
  }

  function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) revert();
    entityStructs[entityAddress].entityData    = entityData;
    return true;
  }
}

Zugeordnete Strukturen mit löschfähigem Index

Stärken

  • Wahlfreier Zugriff durch eindeutige ID oder Zeilennummer
  • Zusicherung der Eindeutigkeit der ID
  • Schließen Sie Arrays, Zuordnungen und Strukturen in jeden "Datensatz" ein
  • Zähle die Rekorde
  • Zählen Sie die IDs auf
  • Kontrollieren Sie logisch die Größe der aktiven Liste mit der Löschfunktion

Schwächen

  • Leicht erhöhte Codekomplexität
  • Geringfügig höhere Lagerkosten
  • Die Schlüsselliste ist von Natur aus ungeordnet

UPDATE, 2019

Dieses Muster ist als Bibliothek für Solidity 0.5.1 verfügbar: https://medium.com/@robhitchens/solidity-crud-epilogue-e563e794fde , https://github.com/rob-Hitchens/UnorderedKeySet

Beispiel:

contract mappedWithUnorderedIndexAndDelete {

  struct EntityStruct {
    uint entityData;
    uint listPointer;
  }

  mapping(address => EntityStruct) public entityStructs;
  address[] public entityList;

  function isEntity(address entityAddress) public constant returns(bool isIndeed) {
    if(entityList.length == 0) return false;
    return (entityList[entityStructs[entityAddress].listPointer] == entityAddress);
  }

  function getEntityCount() public constant returns(uint entityCount) {
    return entityList.length;
  }

  function newEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(isEntity(entityAddress)) revert();
    entityStructs[entityAddress].entityData = entityData;
    entityStructs[entityAddress].listPointer = entityList.push(entityAddress) - 1;
    return true;
  }

  function updateEntity(address entityAddress, uint entityData) public returns(bool success) {
    if(!isEntity(entityAddress)) revert();
    entityStructs[entityAddress].entityData = entityData;
    return true;
  }

  function deleteEntity(address entityAddress) public returns(bool success) {
    if(!isEntity(entityAddress)) revert();
    uint rowToDelete = entityStructs[entityAddress].listPointer;
    address keyToMove   = entityList[entityList.length-1];
    entityList[rowToDelete] = keyToMove;
    entityStructs[keyToMove].listPointer = rowToDelete;
    entityList.length--;
    return true;
  }

}

Letzteres hat hier einen Erklärer: https://medium.com/@robhitchens/solidity-crud-part-2-ed8d8b4f74ec#.ekc22r5lf

und hier: https://bitbucket.org/rhitchens2/soliditycrud/src/83703dcaf4d0c4b0d6adc0377455c4f257aa29a7/docs/?at=master

Beispiel Ordnerbaum: Wie können wir die Speicherung eines Ordners oder Objektbaums in Solidity organisieren?

Das Beispiel einer verknüpften Liste zeigt eine Möglichkeit, eine geordnete Liste mithilfe einer Bibliothek zu verwalten. https://github.com/ethereum/dapp-bin/blob/master/library/linkedList.sol0 _

Kommentare sind nicht für längere Diskussionen gedacht; diese Konversation wurde in den Chat verschoben .

Ergänzend zu Robs Antwort verwenden Sie bitte revert() als Alternative zu throw. Ab Version 0.4.13 ist das throw-Schlüsselwort veraltet und wird in Zukunft auslaufen. Lesen Sie hier, um weitere Informationen zu erhalten: Require, Assert und Revert in Solidity.

Also, als Beispiel sollten Sie ändern

if(isEntity(entityAddress)) throw;

zu

if(isEntity(entityAddress)) revert();

im obigen Code von Rob bereitgestellt.

Hier sind die aktualisierten Codebeispiele mit revert(): ethfiddle.com/PgDM-drAc9
Danke, James! Ich bin sicher, das wird den Leuten helfen. Man könnte auch verwenden require(), aber das würde bedeuten, alle Regeln umzukehren ... if(bad) revert()=>require(!bad)
Was ist der Hauptunterschied zwischen throwund revert()? Tun sie dasselbe, indem sie alle Zustände zurücksetzen? @Abhishek Sinha && @Rob Hitchens
Starten von solc 0.4.13, revertund requireveraltet . Sie weichen in Details wie Gasvernichtung geringfügig vom Original ab. Schau mal hier vorbei: ethereum.stackexchange.com/questions/15166/…assertthrowthrow

Tolle Antwort von Rob Hitchens . Möchte auf eine geringfügige Änderung im erwähnten Code hinweisen. In der newEntityFunktion des simpleListVertrags hat der Autor verwendetreturn entityStructs.push(newEntity)-1;

Seit Solidity Version 0.6.0 gibt die Funktion array.push() nichts zurück. Bitte lesen Sie die Antwort hier: https://ethereum.stackexchange.com/a/87791/73743 . Der return entityStructs.push(newEntity)-1;Teil des Codes bricht also den Smart Contract.

Das ist ganz richtig. Breaking Changes passieren. Hoffentlich verstehen die Leser den Kern dessen, was passiert, und wissen, wie man umgestaltet, oder werfen einen Blick in die Bibliothek, um aktuellere Beispiele zu finden (2017!). array--wurde .popbeispielsweise durch ersetzt.

Danke Rob für die Inspiration!

Hier ist eine erweiterte Version von Mapped Structs mit Delete-fähigem Index , die auch verschachtelte Mappings, "unstrukturierte" Daten und Eigentumsrechte unterstützen

Hoffe, es wird jemandem helfen

Nur-Link-Antworten werden nicht empfohlen. Es ist besser, die Hauptidee in die Antwort aufzunehmen und den Link nur zu verwenden, um die Details zu erweitern.