Solidity bool size en Structs

Estoy tratando de empacar bien mi tipo de datos de estructura.
La pregunta que surgió es ¿cuánto tamaño de almacenamiento ocupa la variable bool ?
Y si suponemos que tomará 1 byte, si pongo consecutivas hasta 8 variables booleanas, ¿intentará la EVM empaquetarlas en 1 byte?
EjemploEstructura

bool no es más que uint8 internamente.

Respuestas (4)

NOTA: Esta respuesta incluye solo datos experimentales

Lea la respuesta de @ medvedev1088 antes, porque mi respuesta es solo la adición y algunos datos de las pruebas.

De mis pruebas en remix obtuve estos datos:

  • Crear 8- true- bool structcuesta 58211 gas; 20k + 7 * 5k = 55k gasolina;
  • Crear 16- true- bool structcuesta 100807 gas; 20k + 15 * 5k = 95k gasolina;
  • Crear 32- true- bool structcuesta 186505 gas; 20k + 31 * 5k = 175k gasolina;
  • Crear 33- true- bool structcuesta 206811 gas; 2 * 20k + 31 * 5k = 195k gasolina;

A partir de estos datos, está claro que la cantidad máxima de booleanos que puede almacenar en una ranura es 32 .


[Actualización 1]

Es posible que haya notado discrepancias en los números de esas líneas. Esos desajustes en realidad son causados ​​​​por algunas tarifas más pequeñas pagadas por diferentes códigos de operación (que se ejecutan para cada nuevo bool en nuestro struct). Permítanme enumerar los más costosos:

  • SLOADcuesta 200 gasolina
  • EXPcuesta 60 gasolina

Ejecuté la transacción en rinkeby. Eche un vistazo a GETH Trace para TxHash .

¿Quiere decir que los números, por ejemplo, 58211 no coinciden con 55k?
También me pregunto por qué no empaquetan 256 bools en una sola ranura.
Para los números que no coinciden, sospecho que el gas adicional proviene de los cálculos, la lectura de datos de entrada y las asignaciones de memoria.
@ medvedev1088 Estoy de acuerdo. Esos desajustes crecen linealmente con el número de booleanos en la estructura. Pero aún así sería bueno saber qué códigos de operación se están ejecutando exactamente.
Encuentro que el rastreador de transacciones de etherscan es muy útil cuando analizo gas rinkeby.etherscan.io/… . Sería bueno tener algo similar en Remix. Hay un depurador allí, pero no muestra todos los pasos con el gas correspondiente.
@ medvedev1088 Ejecuté la transacción en rinkeby. Echa un vistazo aquí . Prácticamente utilicé el código como uno que proporcionaste pero sin memory ex.
@medvedev1088 SLOADcuesta 200 de gasolina + EXP= 60 de gasolina + un montón de pequeñas tarifas para otros códigos de operación. Actualizaré la respuesta más tarde.
Hallazgo interesante. SLOAD tiene sentido porque no puede simplemente sobrescribir el valor de almacenamiento anterior, ya que borrará otros booleanos. Sin embargo, no tengo idea de por qué necesita EXP.

Probé este contrato simple:

pragma solidity 0.4.20;

contract Test {

  struct Example {
  bool v1;
  bool v2;
  bool v3;
  bool v4;
  bool v5;
  bool v6;
  bool v7;
  bool v8;
  }

  Example example;

  function test() public {
    Example memory ex = Example({
    v1: true,
    v2: true,
    v3: true,
    v4: true,
    v5: true,
    v6: true,
    v7: true,
    v8: true
    });

    example = ex;
  }
}

En el ensamblaje generado hay 8 SSTOREoperaciones y todas apuntan a la misma ranura de almacenamiento. El gas para test()es ~76k que es 21k + 20k + 5k * 7. Esto confirma que el bytecode generado primero escribe en la ranura y luego lo actualiza 7 veces.

Agregar más bools a la estructura agrega 5k de gas a test(). Como señaló @Roman Frolov en su respuesta, hasta 32 bools pueden caber en una sola ranura.

Tenga en cuenta que un bool es un uint8 debajo del capó, esto significa que está usando 8 bits mientras que solo necesita 1 bit.

Es más eficiente empaquetar varios valores booleanos en un uint256 y extraerlos con una máscara. Puede almacenar 256 valores booleanos en un solo uint256 (en una estructura, puede ajustar el tamaño del uint para que coincida con lo que necesita).

Puedes usar el siguiente patrón

function getBoolean(uint256 _packedBools, uint256 _boolNumber)
    public view returns(bool)
{
    uint256 flag = (_packedBools >> _boolNumber) & uint256(1);
    return (flag == 1 ? true : false);
}


function setBoolean(
    uint256 _packedBools,
    uint256 _boolNumber,
    bool _value
) public returns(uint256) {
    if (_value)
        _packedBools = _packedBools | uint256(1) << _boolNumber;
        return _packedBools;
    else
        _packedBools = _packedBools & ~(uint256(1) << _boolNumber);
        return _packedBools;
}

Los resultados son diferentes en Solidity 0.6 y en ejecución en EVM posterior a Estambul (Estambul incluyó la actualización de la lógica de consumo de gas para las operaciones de almacenamiento). Usando el mismo contrato de muestra que medvedev1088 sugerido en una respuesta anterior .

el consumo de gas es mucho menor: 21512 gas para configurar 8 bools.