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?
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
Schwächen
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
Schwächen
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
Schwächen
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
Schwächen
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
Schwächen
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
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 _
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.
revert()
: ethfiddle.com/PgDM-drAc9require()
, aber das würde bedeuten, alle Regeln umzukehren ... if(bad) revert()
=>require(!bad)
throw
und revert()
? Tun sie dasselbe, indem sie alle Zustände zurücksetzen? @Abhishek Sinha && @Rob Hitchenssolc 0.4.13
, revert
und require
veraltet . Sie weichen in Details wie Gasvernichtung geringfügig vom Original ab. Schau mal hier vorbei: ethereum.stackexchange.com/questions/15166/…assert
throw
throw
Tolle Antwort von Rob Hitchens . Möchte auf eine geringfügige Änderung im erwähnten Code hinweisen. In der newEntity
Funktion des simpleList
Vertrags 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.
array--
wurde .pop
beispielsweise durch ersetzt.
Paul S
Rob Hitchens