Wie funktioniert der sha3/keccak256-Hash von Solidity?

Dies ist keine Frage zu dem Problem, dass Ethereum einen nicht standardmäßigen sha3 verwendet . Ich habe die richtige JS-Hashing-Bibliothek gefunden und kann übereinstimmende Hashes in JS und Solidity für Byte-Strings erhalten. Was ich mich frage, ist, wie man ein uint darstellt, wenn man es an die JS-Hash-Bibliothek übergibt, damit es zu demselben Hash führt, der von Solidity sha3 erstellt wird.

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

Solidity
sha3(
  1
);
// 0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2
Update: Solidity keccak256liefert identische Ergebnisse wie Solidity, sha3daher gilt alles in diesem Thread für keccak256.

Antworten (3)

Jehans Antwort ist großartig, aber wir müssen noch etwas erklären: Warum sha3(1)produziert Solidity b10e2d...fa0cf6?

Dies liegt daran, dass die sha3-Funktion von solidity ihre Eingaben basierend auf den Argumenttypen hasht . Daher erzeugt der Wert 1einen anderen Hash, wenn er als bytes8, bytes16, bytes32, usw. gespeichert wird. Da sha3(1)er 1als Zahlenliteral übergeben wird, wird er in den kleinsten erforderlichen Typ uint81 konvertiert .

8 Bit passen in 2 Hex-Zeichen, wenn Sie also Ihre Eingabe auf 2 Zeichen auffüllen, erhalten Sie das gleiche Ergebnis in web3:

Javascript:

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

Ebenso können Sie die Zahl auf die Soliditätsseite werfen:

Solidität:

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

Eine Anmerkung zu den BigNumberTypen:

Sie funktionieren nicht automatisch mit web3.sha3. Sie müssen sie zuerst in Hex umwandeln.

Solidität:

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

BEARBEITEN:

Ich habe eine kleine Bibliothek geschrieben, die eine Version davon bereitstellt web3.sha3, die genau dem Verhalten von sha3in Solidity entspricht. Hoffentlich klärt das all Ihre Hashing-Probleme :). https://github.com/raineorshine/solidity-sha3

Solidity verwendet intern HEX-Werte.

> web3.sha3(web3.toHex(1))
"5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2"
Ich bekomme web3.sha3(web3.toHex(1)) === 8a07523229fdc48491a5e56c76620ba40639eb940e6a2fbdf62b2799b4c86643
Dies ist nicht wirklich eine vollständige Antwort, siehe meine Antwort.

Die sha3-Funktion von Solidity hasht die Byte-Darstellung eines uint. Das heißt, die Zahl in Hex (Basis 16), aufgefüllt auf 32 Bytes. 32 leere Bytes in Hex-Darstellung sind 64 Nullen.

Um dies in JS zu tun, können wir das berüchtigte Left-Pad-Paket verwenden:

const jsHashWeb3 = web3.sha3(leftPad((1).toString(16), 64, 0), { encoding: 'hex' })
// b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6
Produziert in Solidity , nicht wie Ihr Code produziert sha3(1). Könntest du erklären? 0x5fe7f9...dcffd2b10e2d...fa0cf6
Ich kenne das Gefühl. Kann ich leider nicht erklären. Vielleicht ein Tippfehler meinerseits, es ist ein paar Monate her. Ich habe das Snippet verwendet, das ich hier gepostet habe: github.com/ethereum/web3.js/issues/445 . Es deckt nicht alle Grenzfälle ab, aber es hat für mich funktioniert.