-
Notifications
You must be signed in to change notification settings - Fork 148
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
167 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
--- | ||
title: Keccak | ||
hide_title: true | ||
description: A comprehensive guide on how to use Keccak-based hashes in o1js. | ||
keywords: | ||
- keccak | ||
- sha3 | ||
- hash | ||
- ethereum | ||
- mina blockchain | ||
- blockchain technology | ||
- data structures | ||
--- | ||
|
||
:::info | ||
|
||
Please note that zkApp programmability is not yet available on Mina Mainnet, but zkApps can now be deployed to Berkeley Testnet. | ||
|
||
::: | ||
|
||
# Keccak (SHA3) | ||
|
||
#### What is Keccak? | ||
|
||
Keccak was developed by a team of cryptographers from Belgium and it was one of the submissions for the National Institute of Standards and Technology (NIST) SHA-3 competition. Keccak was eventually chosen as the winner of the competition and standardized as SHA-3. | ||
Today, Keccak is used in many different applications, including Ethereum, and many others. | ||
|
||
Specifically, Keccak is used in Ethereum to hash addresses, transactions, and blocks. It is also used to hash the state trie, which is a data structure that stores the state of the Ethereum blockchain. | ||
Because of the common usage of Keccak in Ethereum, it has become a key component of o1js and enables developers to verify Ethereum transactions and blocks in o1js. | ||
|
||
#### Keccak and Poseidon | ||
|
||
Developers familar with o1js are likely familar with Poseidon. It is important to briefly explain the differences between Poseidon, a zero-knowledge native hash function, | ||
and Keccak, a hash function that requires binary arethmetic. More specifically, Poseidon operates over the native [Pallas base field](https://electriccoin.co/blog/the-pasta-curves-for-halo-2-and-beyond/) and uses parameters generated specifically for Mina. Because of that, Poseidon is by far the most efficient hash function currently available in o1js. | ||
|
||
Keccak, on the other hand, is a hash function that operates over binary data and is not native to most zero-knowledge proofs. Because of that, Keccak is not as efficient as Poseidon, but it is still very useful for verifying Ethereum transactions and blocks. | ||
So, when choosing what hash function to use, it is important to consider the use case and the data that needs to be hashed. | ||
|
||
#### Basic usage | ||
|
||
This section briefly explains how to use Keccak in o1js. | ||
|
||
Keccak is available in different configurations, which are available under the `Hash` namespace. The following configurations are available: | ||
|
||
- `Hash.SHA3_256`: NIST SHA3 hash function with output size of 256 bits. | ||
- `Hash.SHA3_385`: NIST SHA3 hash function with output size of 384 bits. | ||
- `Hash.SHA3_512`: NIST SHA3 hash function with output size of 512 bits. | ||
- `Hash.Keccak256`: pre-NIST Keccak hash function with output size of 256 bits. _This is the configuration used to hash blocks, transactions and more in Ethereum._ | ||
- `Hash.Keccak384`: pre-NIST Keccak hash function with output size of 384 bits. | ||
- `Hash.Keccak512`: pre-NIST Keccak hash function with output size of 512 bits. | ||
|
||
As previously mentioned, Keccak operates over binary data instead of native Field elements like Poseidon. | ||
For that reason, we introduced a new type to o1js - `Bytes`. `Bytes` is a fixed-length array of bytes that can be used to represent binary data. | ||
Under the hood, `Bytes` is represented as an array of `UInt8` elements. | ||
|
||
In order to use `Bytes`, you need to extend the `Bytes` class and specify the length of bytes. | ||
|
||
```ts | ||
// This creates a Bytes class that represents 16 bytes | ||
class Bytes16 extends Bytes(16) {} | ||
``` | ||
|
||
To initiate your `Bytes16` class with a value, you can use `from`, `fromHex` or `fromString`. | ||
|
||
```ts | ||
// `.from` accepts an array of `number`, `bigint`, `UInt8` elements or a `Uint8Array` or `Bytes` | ||
let bytes = Bytes16.from(new Array(16).fill(0)); | ||
|
||
// converts a hex string to bytes | ||
bytes = Bytes16.fromHex('646f67'); | ||
|
||
// converts a string to bytes | ||
bytes = Bytes16.fromString('dog'); | ||
|
||
// print the contents of `bytes` to the console | ||
bytes.bytes.forEach((b) => console.log(b.toBigInt())); | ||
// [100n, 111n, 103n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n] | ||
``` | ||
|
||
_Note:_ If the input array is smaller than the length of the `Bytes` class, it will be padded with zeros. | ||
|
||
You can also convert a `Bytes` object back to a hex string using `toHex`. | ||
|
||
```ts | ||
class Bytes3 extends Bytes(3) {} | ||
|
||
let bytes = Bytes3.fromHex('646f67'); | ||
let hex = bytes.toHex(); | ||
console.log('hex', hex); | ||
// 646f67 | ||
``` | ||
|
||
Now that we know how to use `Bytes` as input, we can use it to hash data using Keccak. | ||
|
||
```ts | ||
// define a preimage | ||
let preimage = 'The quick brown fox jumps over the lazy dog'; | ||
|
||
// create a Bytes class that represents 43 bytes | ||
class Bytes43 extends Bytes(43) {} | ||
|
||
// convert the preimage to bytes | ||
let preimageBytes = Bytes43.fromString(preimage); | ||
|
||
// hash the preimage | ||
let hash = Hash.SHA3_256.hash(preimageBytes); | ||
|
||
console.log(hash.toHex()); | ||
//69070dda01975c8c120c3aada1b282394e7f032fa9cf32f4cb2259a0897dfc04 | ||
``` | ||
|
||
### Bytes - API reference | ||
|
||
```ts | ||
// creates a Bytes class that represents n bytes (n is 32 in this example) | ||
let n = 32; | ||
class BytesN extends Bytes(n) {} | ||
|
||
// initiate an instance from a hex string | ||
let bytes = BytesN.fromHex('646f67'); | ||
|
||
// initiate an instance from a string | ||
bytes = BytesN.fromString('dog'); | ||
|
||
// initiate an instance from an array of numbers | ||
bytes = BytesN.from([100, 111, 103]); | ||
|
||
// initiate an instance from an array of bigints | ||
bytes = BytesN.from([100n, 111n, 103n]); | ||
|
||
// initiate an instance from an array of UInt8 elements | ||
bytes = BytesN.from([UInt8(100), UInt8(111), UInt8(103)]); | ||
|
||
// initiate an instance from a Uint8Array | ||
bytes = BytesN.from(new Uint8Array([100, 111, 103])); | ||
|
||
// initiate an instance from another Bytes instance | ||
bytes = BytesN.from(BytesN.fromHex('646f67')); | ||
|
||
// convert the bytes to a hex string | ||
const hex = bytes.toHex(); | ||
``` | ||
|
||
### Keccak - API reference | ||
|
||
```ts | ||
// https://keccak.team/keccak.html hash function, pre-NIST specification | ||
// hash bytes using Keccak256 with output size of 256 bits, mainly used in Ethereum | ||
Hash.Keccak256.hash(bytes); | ||
|
||
// hash bytes using Keccak384 with output size of 384 bits | ||
Hash.Keccak384.hash(bytes); | ||
|
||
// hash bytes using Keccak512 with output size of 512 bits | ||
Keccak.Keccak512.hash(bytes); | ||
|
||
// https://csrc.nist.gov/pubs/fips/202/final hash function, official NIST specification | ||
// hash bytes using SHA3_256 with output size of 256 bits | ||
Hash.SHA3_256.hash(bytes); | ||
|
||
// hash bytes using SHA3_384 with output size of 384 bits | ||
Hash.SHA3_384.hash(bytes); | ||
|
||
// hash bytes using SHA3_512 with output size of 512 bits | ||
Hash.SHA3_512.hash(bytes); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters