【译】智能合约 | Solidity中有哪些实用又简单的存储模式?

in solidity •  6 years ago 

本文翻译自:contract development - Are there well-solved and simple storage patterns for Solidity? - Ethereum Stack Exchange

在Solidity智能合约开发中,数据结构的定义是非常重要的,下面介绍几种经常用到的数据结构。

Array(元素为Struct)

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

EntityStruct[] public entityStructs;

优点

  • 可以保证按时间顺序排列
  • 可以获得长度
  • 可以通过数组下标随意取值

缺点

  • 不能通过Id随机取值
  • 不能保证唯一性
  • 无法验证重复值
  • 无法控制数组长度

例子:

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

Mapping(Id => Struct)

struct EntityStruct {
    uint entityData;
    bool isEntity;
}

mapping (address => EntityStruct) public entityStructs;

优点

  • 可以通过Id取值
  • Id唯一
  • 内部可以包含 arrays, mappings和structs

缺点

  • 无法按照key遍历
  • 无法获得元素的数量
  • 元素的值默认是0,如果要区分当前值为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)) throw; 
    entityStructs[entityAddress].entityData = entityData;
    entityStructs[entityAddress].isEntity = true;
    return true;
  }

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

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

Array(元素为Struct) + Mapping(Id => bool)

struct EntityStruct {
    address entityAddress;
    uint entityData;
}

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

优点

  • 可以通过数组下标随意取值
  • Id唯一
  • 内部可以包含 arrays, mappings和structs

缺点

  • 不能通过Id随机取值
  • 无法控制数组长度

例子:

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)) throw;
    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)) throw;
    if(entityStructs[rowNumber].entityAddress != entityAddress) throw;
    entityStructs[rowNumber].entityData    = entityData;
    return true;
  }
}

Mapping(Id => Struct)+ Array(元素为Id)

struct EntityStruct {
    uint entityData;
    bool isEntity;
}

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

优点

  • 可以通过Id数组下标随意取值
  • Id唯一
  • 内部可以包含 arrays, mappings和structs
  • 保留了元素声明的顺序
  • 可以获得元素数量
  • 可以通过Id遍历
  • 通过设置布尔值来删除元素

缺点

  • 无法控制数组长度

例子:

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)) throw;
    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)) throw;
    entityStructs[entityAddress].entityData    = entityData;
    return true;
  }
}

Mapping(Id => Struct)+ Array(元素为Id)+ 删除功能

struct EntityStruct {
    uint entityData;
    uint listPointer;
}

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

优点

  • 可以通过Id数组下标随意取值
  • Id唯一
  • 内部可以包含 arrays, mappings和structs
  • 可以获得元素数量
  • 可以通过Id遍历
  • 通过设置布尔值来删除元素
  • 可以通过删除函数控制剩余元素的数量

缺点

  • 增加了代码复杂度
  • 增加存储成本
  • 元素是无序的

例子:

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)) throw;
    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)) throw;
    entityStructs[entityAddress].entityData = entityData;
    return true;
  }

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

}
Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Hi! I am a robot. I just upvoted you! I found similar content that readers might be interested in:
https://ethereum.meta.stackexchange.com/questions/443/blog-simple-storage-patterns-in-solidity

Congratulations @ljyxxzj! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 2 years!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Vote for @Steemitboard as a witness to get one more award and increased upvotes!