¿Cuánto cuesta almacenar cada hash de IPFS en la cadena de bloques de Ethereum?

He buscado en algunos de los recursos para calcular los costos de Gas como esta hoja de cálculo que encontré en este hilo . Pero la hoja de cálculo solo tiene cálculos de costos de gas pero no almacenamiento de hashing. Estoy un poco confundido, la forma de almacenar el hash de IPFS en el contrato de blockchain también es un poco diferente según este hilo . Pensé que sería sencillo tener que almacenar cadenas o bytes, pero ¿debe estar en Struct? ¿Alguien puede ayudarme? * lo siento, todavía soy nuevo en la programación de contratos inteligentes

SSTORE para una palabra bytes32 es 20k gas
Gracias por responder. Solo quiero aclarar. desde el enlace del segundo hilo. La respuesta usa struct con bytes y uint, ¿verdad? Si entiendo correctamente, es porque ipfs usa los 3 de esos componentes, ¿verdad? ¿Eso significa que el costo total del gas sería el almacenamiento de 32 bytes + uint8 + uint8 y también la operación ADD entre los 3? gracias de nuevo
Puede almacenarlo como bytes32 si lo codifica en base658. Publicaré respuesta

Respuestas (3)

Al almacenar datos en la cadena de bloques de Ethereum, cada tarea de cómputo tiene un costo (en unidades de gas) y, por lo general, desea reducir al máximo el costo de la transacción para su aplicación.

Desea almacenar un hash IPFS (o Multihash) (o CID) como QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u en un contrato inteligente

Primero: Lea esto para entender multihash


Aquí hay tres soluciones para almacenar un hash de IPFS con el costo del gas asociado.

Suponiendo gasPrecio: 20 000 000 000 (wei)

1. Guárdelo como una cadena

Contrato inteligente:

contract IPFSStorage {
    string hash;
    function storeCIDAsString(string _hash) public {
        hash = _hash;
    }
}

Prueba de trufa:

it('should store the IPFS CID as a string', async () => {

    instance.storeCIDAsString(cid, {'from': accounts[0]}).then(function(txReceipt) {
        console.log('# should store the IPFS CID as a string');

        let gasUsed = txReceipt.receipt.gasUsed;
        console.log("gasUsed: " + gasUsed + " units");

        let gasCost = gasUsed*gasPrice;
        console.log("gasCost (wei): " + gasCost + " wei");

        let gasCostEth = web3.fromWei(gasCost, 'ether')
        console.log("gasCost (ether): " + gasCostEth + " ether");
    }).catch(function (error) {
        console.log(error);
    });
});

Resultado:

# should store the IPFS CID as a string
gasUsed: 85986 units
gasCost (wei): 1719720000000000 wei
gasCost (ether): 0.00171972 ether

2. Guárdelo como una estructura

Contrato inteligente:

contract IPFSStorage {
    struct Multihash {
        bytes32 hash;
        bytes2 hash_function;
        uint8 size;
    }

    Multihash multihash;

    function storeCIDAsStruct(bytes32 _hash, bytes2 _hash_function, uint8 _size) public {

        Multihash memory multihashMemory;
        multihash.hash = _hash;
        multihash.hash_function = _hash_function;
        multihash.size = _size;

        multihash = multihashMemory;
    }
}

Prueba de trufa:

it('should store the IPFS CID as a struct', async () => {

    let mh = multihashes.fromB58String(Buffer.from(cid))
    let args = {
      hashFunction: '0x' + mh.slice(0, 2).toString('hex'),
      digest: '0x' + mh.slice(2).toString('hex'),
      size: mh.length - 2
    }

    instance.storeCIDAsStruct(args.digest, args.hashFunction, args.size, {'from': accounts[0]}).then(function(txReceipt) {
        console.log('# should store the IPFS CID as a struct');

        let gasUsed = txReceipt.receipt.gasUsed;
        console.log("gasUsed: " + gasUsed + " units");

        let gasCost = gasUsed*gasPrice;
        console.log("gasCost (wei): " + gasCost + " wei");

        let gasCostEth = web3.fromWei(gasCost, 'ether')
        console.log("gasCost (ether): " + gasCostEth + " ether");
    }).catch(function (error) {
        console.log(error);
    });
});

Resultado:

# should store the IPFS CID as a struct
gasUsed: 55600 units
gasCost (wei): 1112000000000000 wei
gasCost (ether): 0.001112 ether

3. Guárdelo en el registro de eventos

Contrato inteligente:

contract IPFSStorage {
    event CIDStoredInTheLog(string _hash);

    function storeCIDInTheLog(string _hash) public {

        emit CIDStoredInTheLog(_hash);
    }
}

Prueba de trufa:

it('should store the IPFS CID in the logs', async () => {

    instance.storeCIDInTheLog(cid, {'from': accounts[0]}).then(function(txReceipt) {
        console.log('# should store the IPFS CID in the logs');

        let gasUsed = txReceipt.receipt.gasUsed;
        console.log("gasUsed: " + gasUsed + " units");

        let gasCost = gasUsed*gasPrice;
        console.log("gasCost (wei): " + gasCost + " wei");

        let gasCostEth = web3.fromWei(gasCost, 'ether')
        console.log("gasCost (ether): " + gasCostEth + " ether");
    }).catch(function (error) {
        console.log(error);
    });
});

Resultado:

# should store the IPFS CID in the logs
gasUsed: 27501 units
gasCost (wei): 550020000000000 wei
gasCost (ether): 0.00055002 ether

Como puede ver, cada solución funciona, la única diferencia es el costo del gas de la transacción para almacenar un hash de IPFS en Blockchain.

Puedes encontrar el código aquí (Proyecto Trufa)

¡Gracias por responder! ¿Puedo preguntar? ¿Qué es el número 3 (registro de eventos)? ¿El registro de eventos es algo en el bloque? ¿Te gustan las transacciones? ¿Se conservará en blockchain?
En lugar de un comentario largo, aquí hay un buen enlace.
Esta es una respuesta increíble. Definitivamente probaré el método struct que mostraste. Me sorprende lo caras que son las cadenas incluso en comparación con la estructura que implementó.
Almacenar el IPFS CID como un evento con distintos tipos de datos para el bytes1: hashFunction, bytes1: sizey bytes32: digestlo mejora aún más (en ~10 %).

Las cadenas son malas, las estructuras son aún peores en términos de costo.

Este código JS hace que un hash ipfs encaje en un solo tamaño de palabra EVM. Cuál es la forma más eficiente de utilizar el EVM.

El costo de Tx para esto es 21k por transacción + 20k por SSTORE (sin valor nulo)

Alternativamente, puede usar Swarm, que es el sistema de archivos P2P de la pila Ethereum web3. Los hashes de contenido devueltos por Swarm tienen una longitud de 32 bytes, por lo que funcionan muy bien con EVM.

https://swarm-guide.readthedocs.io/en/latest/introduction.html

Swarm es una plataforma de almacenamiento distribuido y un servicio de distribución de contenido, un servicio de capa base nativo de la pila ethereum web3. El objetivo principal de Swarm es proporcionar un almacenamiento suficientemente descentralizado y redundante del registro público de Ethereum, en particular para almacenar y distribuir código y datos dapp, así como datos de blockchain. Desde un punto de vista económico, permite a los participantes agrupar de manera eficiente sus recursos de almacenamiento y ancho de banda para brindar estos servicios a todos los participantes de la red, todo mientras Ethereum los incentiva.

Si no desea escribir su propio envoltorio, puedo recomendarle el uso de Erebos del equipo de mainframe, que incluye un envoltorio para PSS (Swarm+Whisper), servicios postales sobre Swarm, también. https://erebos.js.org/ importar bs58 desde 'bs58'

// Return bytes32 hex string from base58 encoded ipfs hash,
// stripping leading 2 bytes from 34 byte IPFS hash
// Assume IPFS defaults: function:0x12=sha2, size:0x20=256 bits
// E.g. "QmNSUYVKDSvPUnRLKmuxk9diJ6yS96r1TrAXzjTiBcCLAL" -->
// "0x017dfd85d4f6cb4dcd715a88101f7b1f06cd1e009b2327a0809d01eb9c91f231"
function bytes32FromIpfs(ipfsHash) {
  return (
    "0x" +
    bs58
      .decode(ipfsHash)
      .slice(2)
      .toString("hex")
  )
}

// Return base58 encoded ipfs hash from bytes32 hex string,
// E.g. "0x017dfd85d4f6cb4dcd715a88101f7b1f06cd1e009b2327a0809d01eb9c91f231"
// --> "QmNSUYVKDSvPUnRLKmuxk9diJ6yS96r1TrAXzjTiBcCLAL"
function ipfsFromBytes32(bytes32) {
  // Add our default ipfs values for first 2 bytes:
  // function:0x12=sha2, size:0x20=256 bits
  // and cut off leading "0x"
  const hashHex = "1220" + bytes32.slice(2)
  const hashBytes = Buffer.from(hashHex, "hex")
  const hashStr = bs58.encode(hashBytes)
  return hashStr
}

El uso de un evento definido de la siguiente manera para almacenar los datos en los registros mejora aún más los costos de gas. Comparado con la mejor solución que @greag_jeanmart

    event CIDStructStoredInTheLog(
        bytes1 hash_function,
        bytes1 size,
        bytes32 hash
    );

Bifurqué su código y lo actualicé para que funcione con la versión actual de Truffle. Encuentra el código en Github .

gasUsed: 25841 units
gasCost (wei): 516820000000000 wei
gasCost (ether): 0.00051682 ether