¿Cómo podemos organizar el almacenamiento de una carpeta o un árbol de objetos en Solidity?

Inspirado en la colección de basura

Un patrón de almacenamiento jerárquico de objetos dentro de objetos es un dispositivo bastante común. ¿Cómo podría uno organizar tal cosa y evitar problemas con el aumento del costo del gas a escala?

Muchas de este tipo de preguntas se han publicado recientemente, pero en realidad solo se trata de comprender las estructuras de datos clásicas. Esencialmente, las mismas cosas serán eficientes en EVM que en C o Java.

Respuestas (1)

En el espíritu de ¿Existen patrones de almacenamiento sencillos y bien resueltos para Solidity? .

Este es un poco más complicado, pero parece un patrón reutilizable. La estructura básica de cada nodo es:

  1. Un bool para indicar que el nodo es válido
  2. Un puntero clave principal para gatear hacia ARRIBA
  3. Una lista desordenada de claves secundarias
  4. Un número de fila de "este" nodo en la lista de hijos del padre correspondiente

Este patrón intenta eliminar las operaciones de búsqueda ilimitadas en el contrato empujando la mayoría de las funciones recursivas/loco hacia el lado del cliente. Un nodo se puede validar (existe) en un solo movimiento. Las búsquedas y las inserciones utilizan una "pista" que debe estar "aproximadamente" cerca del punto de inserción o la solución. El contrato resolverá la ubicación exacta en la lista ordenada. La sugerencia ayuda a reducir el costo de la gasolina para búsquedas potencialmente costosas. Cuanto mejor sea la pista, menor será el costo.

El patrón admite la poda de ramas y su contenido a cualquier escala con un costo de gas constante y bajo.

En el caso de que un cliente quiera validar que un nodeId no está dentro de una rama podada, el cliente debe explorar recursivamente los padres hasta que se encuentre el padre "raíz" (tiene padre == 0) y confirmar que isNode == true . .. de lo contrario, la clave para verificar está dentro de una rama podada.

Ejemplo:

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

}

Espero eso ayude.