Acceso a un valor de matriz de tamaño dinámico en un contrato

Estoy intentando tengo este contrato:

pragma solidity ^0.4.2;

contract ArrayContract {
    address  _owner;
    uint256[] array;

    function ArrayContract (){
        _owner = msg.sender;
    array.push(1);
    array.push(2);
    array.push(3);
    }
}

Entonces, como muchos otros, estoy tratando de acceder a los datos usando el mensaje RPC 'getStorageAt'.

Cuando trato de acceder al campo de la matriz (campo 1) tengo como respuesta el número 3. Tiene sentido. Es el tamaño de la matriz.

Entonces, teniendo en cuenta otras publicaciones ( ¿Cómo obtengo los índices/claves de almacenamiento? web3.eth.getStorageAt para el mapeo ) Y teniendo en cuenta la documentación:

https://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage

Intento llamar al RPC con el parámetro keccak256(1) (El resultado de este cálculo es el hash: 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6)

Entonces tengo algo como

web3.getStorageAt('account', 'c89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6', 'latest'); 

Y el resultado sigue vacío (0x000....00).

======= También probé, sin éxito, con los siguientes índices:

index = keccak256(1 . 1) (la posición de la matriz en el contrato y la primera posición de la matriz)

índice = keccak256(keccak256(1).keccak256(1))

=======

Empiezo a sospechar que esto no funciona. ¿Alguien puede indicarme una solución a este problema?

Muchas gracias :)

================ EDICIÓN ========================

Hola a todos. Finalmente volví a este tema. Después de implementar las cosas necesarias para mi plataforma (no estoy usando javascript sino smalltalk), puedo generar el hash necesario para acceder al mensaje getStorageAt con la codificación adecuada. Usando este mismo ejemplo, la ranura relacionada con este código es el número 1. De acuerdo con la documentación, debería obtener el contenido de la matriz ejecutando:

web3.getStorageAt('cuenta', 'b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6', 'más reciente');

El resultado que da el servicio GETH RPC a esta línea es: 0000000000000000000000000000000000000000000000000000000000000001

Eso pasa a ser el primer elemento en la matriz. (Verifiqué con otras órdenes para estar seguro, y de hecho es el primer elemento de la matriz)

Luego, la siguiente pregunta relacionada es, ¿cómo obtengo los siguientes elementos? De acuerdo con la documentación, esta llamada debería devolver la matriz completa.

Solo como prueba, traté de acceder a cada elemento usando la forma de mapeo usando diferentes codificaciones de enteros sin signo para el índice del elemento en la matriz:

index = keccak256(ENC ElementID . ENC SlotID)

índice = keccak256(uint8 1 . uint128 1)

...

índice = keccak256(uint128 1 . uint128 1)

índice = keccak256(uint256 1 . uint128 1)

Sin ningún tipo de éxito.

¿Tienes alguna pista sobre lo que me estoy perdiendo? ¡Muchas gracias!

santiago

Respuestas (2)

El problema es que el keccak de RPC es ligeramente diferente al que se usa en Solidiy. Para obtener hashes coherentes con la solidez, use la biblioteca ethereumjs-abi . Por ejemplo:

var abi = require('ethereumjs-abi')
var BN = require('bn.js')

abi.soliditySHA3(
    ["uint"],
    [ new BN(1)]
).toString('hex')

> "b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"

Este es el hash correcto que debes usar. Además, asegúrese de pasar el valor hexadecimal actual a getStorageAt, es decir, pasando el resultado de abi.soliditySHA3sin convertirlo en una cadena hexadecimal.

¿Hay algún detalle de cómo el Solidity keccak es diferente?

En realidad, si su algoritmo hash le da c89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6cuando hash 1, entonces es el algoritmo correcto.

Es solo que debe codificar el valor correctamente antes de usar la función hash, es decir, debe usar la cadena hexadecimal 0000000000000000000000000000000000000000000000000000000000000001en lugar de simplemente 1.

En el patio de recreo de Ethers.js :

> utils.keccak256(49) # 49 is the int value of ASCII char '1'
"0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6"

> utils.solidityPack(['uint256'], [1])
"0x0000000000000000000000000000000000000000000000000000000000000001"

> utils.keccak256(utils.solidityPack(['uint256'], [1]))
"0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"

> utils.solidityKeccak256(['uint256'], [1])
"0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"

Para acceder al segundo elemento de la matriz en la ranura de almacenamiento 1, no debe hacer keccak256(pack(2))(esa sería la ubicación de almacenamiento de una matriz dinámica almacenada en la ranura de almacenamiento 2), pero keccak256(pack(1)) + 1, es decir, 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf7.