Wie können wir die Speicherung eines Ordners oder Objektbaums in Solidity organisieren?

Inspiriert von der Müllabfuhr

Ein hierarchisches Speichermuster von Objekten innerhalb von Objekten ist ein ziemlich verbreitetes Mittel. Wie könnte man so etwas organisieren und Probleme mit steigenden Gaskosten im großen Maßstab vermeiden?

Viele dieser Art von Fragen wurden in letzter Zeit gepostet, aber eigentlich geht es nur darum, klassische Datenstrukturen zu verstehen. Auf der EVM werden im Wesentlichen die gleichen Dinge effizient sein wie in C oder Java.

Antworten (1)

Ganz im Sinne von Gibt es gut gelöste und einfache Speichermuster für Solidity? .

Dieses ist etwas komplizierter, aber es scheint ein wiederverwendbares Muster zu sein. Die Grundstruktur jedes Knotens ist:

  1. Ein boolescher Wert, der angibt, dass der Knoten gültig ist
  2. Ein übergeordneter Schlüsselzeiger zum Crawlen nach oben
  3. Eine ungeordnete Liste von untergeordneten Schlüsseln
  4. Eine Zeilennummer von "diesem" Knoten in der Liste der Kinder des entsprechenden Elternteils

Dieses Muster versucht, unbegrenzte Suchvorgänge im Vertrag zu eliminieren, indem die meisten rekursiven/schleifenartigen Funktionen auf die Clientseite verschoben werden. Ein Knoten kann in einem Zug validiert werden (existiert). Suchen und Einfügen verwenden einen "Hinweis", der sich "ungefähr" in der Nähe des Einfügepunkts oder der Lösung befinden sollte. Der Vertrag wird den genauen Standort in der bestellten Liste auflösen. Der Hinweis hilft, die Benzinkosten für möglicherweise kostenintensive Suchen zu reduzieren. Je besser der Hinweis, desto geringer die Kosten.

Das Muster unterstützt das Beschneiden von Ästen und deren Inhalten in jedem Maßstab mit gleichbleibend niedrigen Gaskosten.

Für den Fall, dass ein Client validieren möchte, dass sich eine nodeId nicht in einem beschnittenen Zweig befindet, sollte der Client die Eltern rekursiv untersuchen, bis der „Root“-Elternteil (hat parent == 0) gefunden wird, und bestätigen, dass isNode == true ist. .. andernfalls befindet sich der zu überprüfende Schlüssel in einem beschnittenen Zweig.

Beispiel:

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

}

Ich hoffe es hilft.