Существуют ли хорошо решенные и простые шаблоны хранения для Solidity?

Простая и правильная организация данных может бросить вызов новичкам Solidity. Он хочет, чтобы мы организовали все таким образом, к которому многие из нас не привыкли.

Существуют ли хорошо решенные общие шаблоны для рутинной организации данных в сети?

какое хранилище? Я отмечаю, что в примерах еще нет отсортированного хранилища.
Библиотека Solidity CRUD (2019) реализует сопоставленные структуры с удалением: medium.com/robhitchens/solidity-crud-epilogue-e563e794fde

Ответы (4)

Вот несколько простых и полезных шаблонов в порядке возрастания полезности.

Журналы событий опущены для краткости. На практике желательно генерировать события для каждого важного изменения состояния.

Простой список с использованием массива

Сильные стороны

  • Достоверно хронологический порядок
  • Обеспечивает счет
  • Произвольный доступ по номеру строки (не по идентификатору)

Слабые стороны

  • Нет произвольного доступа по идентификатору
  • Нет гарантии уникальности
  • Нет проверки на дубликаты
  • Неконтролируемый рост списка

Пример:

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;
  }
}

Отображение со структурой

Сильные стороны

  • Произвольный доступ по уникальному идентификатору
  • Гарантия уникальности идентификатора
  • Вложите массивы, сопоставления, структуры в каждую «запись».

Слабые стороны

  • Невозможно перечислить ключи
  • Не могу сосчитать ключи
  • Требуется ручная проверка, чтобы отличить запись по умолчанию от явной записи «все 0».

Пример:

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

и здесь: https://bitbucket.org/rhitchens2/soliditycrud/src/83703dcaf4d0c4b0d6adc0377455c4f257aa29a7/docs/?at=master

Пример дерева папок: как мы можем организовать хранение дерева папок или объектов в 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-drAc9
Спасибо, Джеймс! Я уверен, что это поможет людям. Можно было бы и использовать require(), но это означало бы изменение всех правил на противоположные... if(bad) revert()=>require(!bad)
В чем основная разница между throwи revert()? Делают ли они то же самое, возвращая назад все состояния? @Абхишек Синха && @Роб Хитченс
Начиная solc 0.4.13, и устареть . revert_ Они немного отличаются от оригинала такими деталями, как газовое разрушение. Посмотрите здесь: ethereum.stackexchange.com/questions/15166/…requireassertthrowthrow

Отличный ответ Роба Хитченса . Хотел бы указать на незначительное критическое изменение в упомянутом коде. В 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;часть кода нарушает смарт-контракт.

Совершенно верно. Происходят критические изменения. Надеюсь, читатели поймут суть происходящего и узнают, как проводить рефакторинг, или заглянут в библиотеку для получения более свежих примеров (2017!). array--был заменен на .pop, например.

Спасибо , Роб, за вдохновение!

Вот расширенная версия сопоставленных структур с индексом с поддержкой удаления, которые также поддерживают вложенные сопоставления, «неструктурированные» данные и права собственности .

Надеюсь, это поможет кому-то

Ссылка только на ответ не рекомендуется. Лучше включить в ответ основную мысль и использовать ссылку только для раскрытия деталей.