¿Cuál es la mejor práctica para almacenar y recuperar grandes datos en contratos inteligentes de solidez?

Supongamos que tengo una página de creación de eventos con algunos campos de formulario en la interfaz de usuario. Y tengo un contrato inteligente para almacenar el evento en el mapeo de eventos o la matriz pública en algún formato de estructura.

struct event{
      bytes32 name;
      uint time;
}

event[] public events;

or

mapping(uint=>event) public events;

Estoy enviando la interfaz de usuario y los datos se almacenan en el contrato.

Tengo una página de lista de eventos, que mostrará todos los eventos creados en el contrato.

Después de buscar en muchos foros, obtuve información, ¿alguien puede validar esto?

Enfoque 1:

Por cada envío o guardado en el contrato, se llama a un evento y la interfaz de usuario tiene el observador y se almacena en el almacenamiento local y se muestra en la interfaz de usuario. Los datos de eventos pueden ser un mapeo o una matriz aquí.

No quiero tener ningún almacenamiento intermedio para la aplicación, por lo que cada vez que se actualice la interfaz de usuario, el observador de eventos recibirá todos los datos de estado y volverá a actualizar el almacenamiento local y se mostrará en la interfaz de usuario.

Enfoque 2:

Para el envío de eventos, se crea un nuevo objeto de evento y se inserta en la matriz de eventos.

Y escribiendo el método get events, para iterar la matriz de eventos y devolver la lista de elementos de cadena para cada clave en el objeto y luego concatenar y desestructurar nuevamente las cadenas en una matriz de objetos en la interfaz de usuario para enlazar con la interfaz de usuario.

Creo que el cálculo para el segundo enfoque será alto debido a la iteración. Dado que estamos desarrollando el Dapp, no quiero volver a tener ninguna base de datos intermedia que se convierta en un sistema centralizado.

¿Alguien puede explicar las diversas posibilidades de almacenar y recuperar datos del contrato inteligente, que es una forma preferible hasta ahora?

Me ayudará mucho. Gracias de antemano.

Respuestas (3)

eventes una palabra clave en Solidity que le permite recuperar fácilmente los datos que generó un contrato en una interfaz de Javascript. Es más barato en términos de gas escribir un evento que escribir en una variable de almacenamiento (como su structmatriz). Luego puede .watchpara estos eventos y llevarlos a su interfaz de usuario. Esta es la forma recomendada de hacerlo. Por razones de rendimiento, es posible que desee almacenar en caché estos datos de eventos de alguna manera porque analizar todos los bloques en cada recarga es bastante ineficiente.

Además, no intente incluir todos sus datos en su contrato inteligente. El trabajo de un Dapp bien diseñado es contener nada más que los elementos y punteros a estructuras externas absolutamente necesarios , pero mantener su capa de almacenamiento separada de la cadena de bloques. Se podría implementar una capa de almacenamiento a través de IPFS o Swarm .

Como dijo Sebastian, debe tener cuidado con los datos que desea almacenar en la cadena de bloques en el contrato.

Dicho esto, el costo del gas para almacenar los datos recaerá en el emisor de la transacción para crear un evento. Del mismo modo, cualquier llamada basada en "transacción" que necesite iterar estos datos será muy costosa según la cantidad de datos y las iteraciones que necesite hacer.

Si una función se marca como constante y no requiere la modificación de los datos, se puede llamar sin incurrir en un costo de gas y, por lo tanto, iterar una lista de eventos aunque es costoso en el cálculo si se ejecuta como una transacción es básicamente gratis para llamar.

Una buena práctica sería agregar un rango que desee enumerar, de modo que pueda limitar el procesamiento requerido en cada llamada y paginar los resultados en su dApp.

Aquí hay un ejemplo de un contrato que puede hacer esto, de ninguna manera está completo, pero copiar y pegar en Remix funcionará para mostrar la funcionalidad básica:

pragma solidity ^0.4.0;

contract EventManager {

    struct Event {
        bytes32 name;
        uint time;
    }

    uint totalEvents;

    mapping(uint => Event) EventList;

    event EventAdded(address indexed _senderAddress, uint _eventId);

    function addEvent(bytes32 _name, uint _time) returns(uint eventId) {
        eventId = totalEvents++;
        EventList[eventId] = Event(_name, _time);
        EventAdded(msg.sender, eventId);
    }

    function listEvents(uint _start, uint _count) constant returns(uint[] eventIds, bytes32[] eventNames, uint[] eventTimes) {

        uint maxIters = _count;
        if( (_start+_count) > totalEvents) {
            maxIters = totalEvents - _start;
        }

        eventIds = new uint[](maxIters);
        eventNames = new bytes32[](maxIters);
        eventTimes = new uint[](maxIters);

        for(uint i=0;i<maxIters;i++) {
            uint eventId = _start + i;

            Event memory e = EventList[eventId];
            eventIds[i] = eventId;
            eventNames[i] = e.name;
            eventTimes[i] = e.time;
        }
    }
}

TL/DR: use una asignación con una matriz dinámica:

mapping(address => DataStruct[]) public dataStructsOfOwner;

Si alguien pudiera guiarme sobre dónde publicar mejor esta respuesta, adelante.

Tenía otra tarea: guardar contratos asignados a un propietario. He hecho una pequeña simulación con estas dos opciones.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

struct Contract {
    string name;
    address contractAddress;
}

/**
 * @dev This Database saves an array of contracts for each owner
 */
contract Database1 {
    mapping(address => Contract[]) public contractsOfOwner;

    function addContract(
        address owner,
        string memory name,
        address contractAddress
    ) public {
        contractsOfOwner[owner].push(Contract(name, contractAddress));
    }

    function getContractsOfOwner(address owner)
        external
        view
        returns (Contract[] memory contracts)
    {
        return contractsOfOwner[owner];
    }
}

y

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

struct Contract {
    string name;
    address contractAddress;
    address owner;
}

/**
 * @dev This Database saves an array of contracts and iterates over them to get
 * contacts by owner
 */
contract Database2 {
    address[] contracts;
    mapping(address => Contract) contractDetails;
    mapping(address => uint256) balanceOf;

    function addContract(
        address owner,
        string memory name,
        address contractAddress
    ) public {
        contracts.push(contractAddress);
        contractDetails[contractAddress] = Contract(
            name,
            contractAddress,
            owner
        );
        balanceOf[owner] += 1;
    }

    function getContractsOfOwner(address owner)
        external
        view
        returns (Contract[] memory returncontracts)
    {
        uint256 count = balanceOf[owner];
        Contract[] memory contractsOfOwner = new Contract[](count);
        uint256 i;
        uint256 resultIndex;
        for (i = 0; i < contracts.length; i++) {
            if (contractDetails[contracts[i]].owner == owner) {
                contractsOfOwner[resultIndex] = contractDetails[contracts[i]];
                resultIndex++;
            }
        }
        return contractsOfOwner;
    }
}

Resultado sorprendente:

    ·--------------------------------------|---------------------------|-------------|-----------------------------·
|         Solc version: 0.8.10         ·  Optimizer enabled: true  ·  Runs: 200  ·  Block limit: 30000000 gas  
·······································|···························|·············|······························
|  Methods                             ·              111 gwei/gas               ·       3983.14 eur/eth       
·················|·····················|·············|·············|·············|···············|··············
|  Contract      ·  Method             ·  Min        ·  Max        ·  Avg        ·  # calls      ·  eur (avg)  │
·················|·····················|·············|·············|·············|···············|··············
|  Database1     ·  addContract        ·      72947  ·      90059  ·      81500  ·            4  ·      36.03  
·················|·····················|·············|·············|·············|···············|··············
|  Database2     ·  addContract        ·     122359  ·     156559  ·     135187  ·            4  ·      59.77  
·················|·····················|·············|·············|·············|···············|···························|···············|··············
|  Deployments                         ·                                         ·  % of limit   ·             
·······································|·············|·············|·············|···············|··············
|  Database1                           ·          -  ·          -  ·     406176  ·        1.4 %  ·     179.58  
·······································|·············|·············|·············|···············|··············
|  Database2                           ·          -  ·          -  ·     437581  ·        1.5 %  ·     193.47  
·······································|·············|·············|·············|···············|··············
·--------------------------------------|-------------|-------------|-------------|---------------|-------------·