¿Cómo funciona el hash sha3 / keccak256 de Solidity?

Esta no es una pregunta sobre el problema con Ethereum usando un sha3 no estándar . Encontré la biblioteca de hashing JS correcta y puedo obtener hashes coincidentes en JS y Solidity en cadenas de bytes. Lo que me pregunto es cómo representar un uint al pasarlo a JS hash lib para que resulte en el mismo hash creado por Solidity sha3.

JS
'0x' + keccak(
  1
)
// 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470

Solidity
sha3(
  1
);
// 0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2
Actualización: Solidity keccak256produce resultados idénticos a los suyos, sha3por lo que todo en este hilo se aplica a keccak256.

Respuestas (3)

La respuesta de Jehan es excelente, pero debemos explicar una cosa más: ¿Por qué produce sha3(1)in solidity b10e2d...fa0cf6?

Esto se debe a que la función sha3 de solidity codifica sus entradas en función de los tipos de argumento . Por lo tanto, el valor 1generará un hash diferente si se almacena como bytes8, bytes16, bytes32, etc. Dado sha3(1)que se pasa 1como un número literal, se convierte en el tipo necesario más pequeño, uint81 .

8 bits caben en 2 caracteres hexadecimales, por lo que si rellena su entrada con 2 caracteres, obtendrá el mismo resultado en web3:

JavaScript:

web3.sha3(leftPad((1).toString(16), 2, 0), { encoding: 'hex' })
// 5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2

Del mismo modo, puedes lanzar el número en el lado de la solidez:

Solidez:

// uint is equivalent to uint256
sha3(uint(1))
// b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6

JavaScript:

// note that the value is padded by 64 characters to fit 256 bits
web3.sha3(leftPad((1).toString(16), 64, 0), { encoding: 'hex' })
// b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6

Una nota sobre los BigNumbertipos:

No funcionan automáticamente con web3.sha3. Tienes que convertirlos a hexadecimal primero.

Solidez:

sha3(uint(100 ether))
// c7cc234d21c9cfbd4632749fd77669e7ae72f5241ce5895e410c45185a469273

JavaScript:

// the .slice is to remove the leading '0x'
web3.sha3(leftPad(web3.toHex(web3.toWei(100)).slice(2).toString(16), 64, 0), { encoding: 'hex' })
// c7cc234d21c9cfbd4632749fd77669e7ae72f5241ce5895e410c45185a469273

EDITAR:

Escribí una pequeña biblioteca que proporciona una versión web3.sha3que coincide exactamente con el comportamiento de sha3Solidity. Esperemos que esto aclare todos sus problemas de hash :). https://github.com/raineorshine/solidity-sha3

Solidity usa valores HEX internamente.

> web3.sha3(web3.toHex(1))
"5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2"
Obtengo web3.sha3(web3.toHex(1)) === 8a07523229fdc48491a5e56c76620ba40639eb940e6a2fbdf62b2799b4c86643
Esta no es realmente una respuesta completa, mira mi respuesta.

La función sha3 de Solidity codifica la representación de bytes de un uint. Es decir, el número en hexadecimal (base 16), rellenado a 32 bytes. 32 bytes vacíos en representación hexadecimal son 64 ceros.

Para hacer esto en JS, podemos utilizar el infame paquete del panel izquierdo:

const jsHashWeb3 = web3.sha3(leftPad((1).toString(16), 64, 0), { encoding: 'hex' })
// b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6
En Solidity, sha3(1)produce 0x5fe7f9...dcffd2, no b10e2d...fa0cf6como produce su código. ¿Podrías explicar?
Conozco el sentimiento. Lamentablemente no puedo explicar. Tal vez un error tipográfico de mi parte, han pasado un par de meses. He estado usando el fragmento que publiqué aquí: github.com/ethereum/web3.js/issues/445 . No cubre todos los casos extremos, pero ha estado funcionando para mí.