Простая и правильная организация данных может бросить вызов новичкам Solidity. Он хочет, чтобы мы организовали все таким образом, к которому многие из нас не привыкли.
Существуют ли хорошо решенные общие шаблоны для рутинной организации данных в сети?
Вот несколько простых и полезных шаблонов в порядке возрастания полезности.
Журналы событий опущены для краткости. На практике желательно генерировать события для каждого важного изменения состояния.
Простой список с использованием массива
Сильные стороны
Слабые стороны
Пример:
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;
}
}
Отображение со структурой
Сильные стороны
Слабые стороны
Пример:
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;
}
}
Массив структур с уникальными идентификаторами
Сильные стороны
Слабые стороны
Пример:
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;
}
}
Сопоставленные структуры с индексом
Сильные стороны
Слабые стороны
Пример:
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;
}
}
Сопоставленные структуры с индексом с поддержкой удаления
Сильные стороны
Слабые стороны
ОБНОВЛЕНИЕ, 2019 г.
Этот шаблон доступен в виде библиотеки для Solidity 0.5.1: https://medium.com/@robhitchens/solidity-crud-epilogue-e563e794fde , https://github.com/rob-Hitchens/UnorderedKeySet .
Пример:
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;
}
}
У последнего есть объяснение здесь: https://medium.com/@robhitchens/solidity-crud-part-2-ed8d8b4f74ec#.ekc22r5lf
Пример дерева папок: как мы можем организовать хранение дерева папок или объектов в Solidity?
В примере со связанным списком показан способ ведения упорядоченного списка с помощью библиотеки. https://github.com/ethereum/dapp-bin/blob/master/library/linkedList.sol 0
Добавляя к ответу Роба, используйте revert() в качестве альтернативы throw. Начиная с версии 0.4.13, ключевое слово throw устарело и будет постепенно прекращено в будущем. Прочтите здесь для получения дополнительной информации: require, assert и revert in solidity.
Итак, в качестве примера вы должны изменить
if(isEntity(entityAddress)) throw;
к
if(isEntity(entityAddress)) revert();
в приведенном выше коде, предоставленном Робом.
revert()
: ethfiddle.com/PgDM-drAc9require()
, но это означало бы изменение всех правил на противоположные... if(bad) revert()
=>require(!bad)
throw
и revert()
? Делают ли они то же самое, возвращая назад все состояния? @Абхишек Синха && @Роб Хитченсsolc 0.4.13
, и устареть . revert
_ Они немного отличаются от оригинала такими деталями, как газовое разрушение. Посмотрите здесь: ethereum.stackexchange.com/questions/15166/…require
assert
throw
throw
Отличный ответ Роба Хитченса . Хотел бы указать на незначительное критическое изменение в упомянутом коде. В newEntity
функции simpleList
договора автор использовалreturn entityStructs.push(newEntity)-1;
Начиная с Solidity версии 0.6.0, функция array.push() ничего не возвращает. Пожалуйста, обратитесь к ответу здесь: https://ethereum.stackexchange.com/a/87791/73743 . Итак, return entityStructs.push(newEntity)-1;
часть кода нарушает смарт-контракт.
array--
был заменен на .pop
, например.
Пол С
Роб Хитченс