Как организовать хранение папки или дерева объектов в Solidity?

Вдохновленный сборкой мусора

Иерархический шаблон хранения объектов внутри объектов является довольно распространенным устройством. Как можно было организовать такое и избежать проблем с увеличением стоимости газа при масштабировании?

В последнее время было опубликовано много вопросов такого типа, но на самом деле все сводится к пониманию классических структур данных. По сути, на EVM будут эффективны те же вещи, что и на C или Java.

Ответы (1)

В духе Есть ли хорошо решенные и простые шаблоны хранения для Solidity? .

Это немного сложнее, но похоже на повторно используемый шаблон. Базовая структура каждого узла:

  1. Логическое значение, указывающее, что узел действителен.
  2. Указатель родительского ключа для обхода UP
  3. Неупорядоченный список дочерних ключей
  4. Номер строки "этого" узла в соответствующем родительском списке дочерних элементов.

Этот шаблон пытается устранить неограниченные операции поиска в контракте, передавая большинство рекурсивных/циклических функций на сторону клиента. Узел может быть проверен (существует) за один ход. Поиск и вставка используют «подсказку», которая должна быть «приблизительно» рядом с точкой вставки или решением. Контракт определит точное место в упорядоченном списке. Подсказка помогает снизить стоимость газа для потенциально дорогостоящих поисков. Чем лучше подсказка, тем ниже стоимость.

Шаблон поддерживает обрезку ветвей и их содержимого в любом масштабе с постоянной низкой стоимостью газа.

В случае, если клиент хочет проверить, что nodeId не находится внутри обрезанной ветки, клиент должен рекурсивно исследовать родителей, пока не будет найден «корневой» родитель (имеет parent == 0), и подтвердить, что isNode == true . .. в противном случае ключ для проверки находится внутри обрезанной ветки.

Пример:

pragma solidity ^0.4.6; 

// Simple, Scalable Object Tree 
// Supports top-down tree exploration
// and pruning of branches. 

// Random node membership can be confirmed client-side.
// Crawl parents recursively and confirm root node (parent=0) isNode==true. 
// Not the case for members of pruned branches. 

contract ObjectTree {

    bytes32 public treeRoot;

    struct NodeStruct {
        bool isNode;
        bytes32 parent; // the id of the parent node
        uint parentIndex; //  the position of this node in the Parent's children list
        bytes32[] children; // unordered list of children below this node
        // more node attributes here
    }

    mapping(bytes32 => NodeStruct) public nodeStructs;

    event LogNewNode(address sender, bytes32 nodeId, bytes32 parentId);
    event LogDelNode(address sender, bytes32 nodeId);

    function ObjectTree() {
        treeRoot = newNode(0);
    }

    function isNode(bytes32 nodeId)
        public
        constant
        returns(bool isIndeed)
    {
        return nodeStructs[nodeId].isNode;
    }

    function newNode(bytes32 parent) 
        public
        returns(bytes32 newNodeId)
    {
        if(!isNode(parent) && parent > 0) throw; // zero is a new root node
        newNodeId = sha3(parent, msg.sender, block.number);
        NodeStruct memory node;
        node.parent = parent;
        node.isNode = true;
        // more node atributes here
        if(parent>0) {
            node.parentIndex = registerChild(parent,newNodeId);
        }
        nodeStructs[newNodeId] = node;
        LogNewNode(msg.sender, newNodeId, parent);
        return newNodeId;
    }

    /*
    Depends entirely on the attributes you want to store in the nodes

    function updateNode(bytes32 nodeId, attr ... )
        public
        returns(bool success)
    {
        nodeStructs[nodeId].attrib = attrib];
        Log ... 
        return true;
    }
    */

    function registerChild(bytes32 parentId, bytes32 childId)
        private
        returns(uint index)
    {
        return nodeStructs[parentId].children.push(childId) - 1;
    }

    // Invalidates and detaches node to prune. 
    // Does not invalidate recursively (scalability). 
    // Top-Down crawl will avoid pruned branches. 
    // Bottom-Up validation will find apparent "root" isNode==false. 

    function pruneBranch(bytes32 nodeId)
        public
        returns(bool success)
    {
        bytes32 parent = nodeStructs[nodeId].parent;
        uint rowToDelete = nodeStructs[nodeId].parentIndex;
        uint rowToMove = nodeStructs[parent].children.length-1; // last child in the list
        nodeStructs[parent].children[rowToDelete] = nodeStructs[parent].children[rowToMove];
        nodeStructs[nodeStructs[parent].children[rowToMove]].parentIndex = rowToMove;
        nodeStructs[parent].children.length--;
        nodeStructs[nodeId].parent=0;
        nodeStructs[nodeId].parentIndex=0;
        nodeStructs[nodeId].isNode = false;
        LogDelNode(msg.sender, nodeId);
        return true;
    }

    function getNodeChildCount(bytes32 nodeId)
        public
        constant
        returns(uint childCount)
    {
        return(nodeStructs[nodeId].children.length);
    }

    function getNodeChildAtIndex(bytes32 nodeId, uint index) 
        public 
        constant
        returns(bytes32 childId)
    {
        return nodeStructs[nodeId].children[index];
    }

}

Надеюсь, это поможет.