Как хеш-код Solidity sha3/keccak256 хеширует?

Это не вопрос о проблеме с Ethereum, использующим нестандартный sha3 . Я нашел правильную библиотеку хеширования JS и могу получить совпадающие хэши в JS и Solidity для байтовых строк. Мне интересно, как представить uint при передаче его в хеш-библиотеку JS, чтобы он приводил к тому же хешу, что и созданный Solidity sha3.

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

Solidity
sha3(
  1
);
// 0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2
Обновление: Solidity keccak256дает идентичные результаты, sha3поэтому все в этой теме относится к файлам keccak256.

Ответы (3)

Ответ Жеана великолепен, но нам нужно объяснить еще одну вещь: почему sha3(1)в твердости создается b10e2d...fa0cf6?

Это связано с тем, что функция sha3 Solidity хэширует свои входные данные на основе типов аргументов . Таким образом, значение 1будет генерировать другой хэш, если оно хранится как bytes8, bytes16, bytes32и т. д. Поскольку sha3(1)передается 1как числовой литерал, оно преобразуется в наименьший необходимый тип, uint81 .

8 бит умещаются в 2 шестнадцатеричных символа, поэтому, если вы дополните свой ввод двумя символами, вы получите тот же результат в web3:

Javascript:

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

Точно так же вы можете указать число на стороне твердости:

Прочность:

// 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

Примечание о BigNumberтипах:

Они не работают автоматически с web3.sha3. Сначала вы должны преобразовать их в шестнадцатеричный формат.

Прочность:

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

РЕДАКТИРОВАТЬ:

Я написал небольшую библиотеку, которая предоставляет версию web3.sha3, точно соответствующую поведению sha3в Solidity. Надеюсь, это прояснит все ваши проблемы с хэшированием :). https://github.com/raineorshine/solidity-sha3

Solidity использует шестнадцатеричные значения для внутреннего использования.

> web3.sha3(web3.toHex(1))
"5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2"
Я получаю web3.sha3(web3.toHex(1)) === 8a07523229fdc48491a5e56c76620ba40639eb940e6a2fbdf62b2799b4c86643
На самом деле это не полный ответ, см. Мой ответ.

Функция Solidity sha3 хэширует байтовое представление uint. То есть число в шестнадцатеричном формате (с основанием 16), дополненное до 32 байт. 32 пустых байта в шестнадцатеричном представлении — это 64 нуля.

Чтобы сделать это в JS, мы можем использовать печально известный пакет left-pad:

const jsHashWeb3 = web3.sha3(leftPad((1).toString(16), 64, 0), { encoding: 'hex' })
// b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6
В Solidity sha3(1)производит 0x5fe7f9...dcffd2не так, b10e2d...fa0cf6как производит ваш код. Могли бы вы объяснить?
Я знаю это чувство. К сожалению, я не могу объяснить. Может опечатка с моей стороны, прошло пару месяцев. Я использовал фрагмент, который я разместил здесь: github.com/ethereum/web3.js/issues/445 . Он не охватывает все крайние случаи, но он работает для меня.