Skip to content
This repository has been archived by the owner on May 3, 2024. It is now read-only.

BLAKE2b hash function and BLAKE2b-512 compression function of EIP-152 #56

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
9dbcce9
Create Cargo.toml
AlekseiVambol Jan 17, 2023
4e867da
Add files via upload
AlekseiVambol Jan 17, 2023
e5d6ec2
Add files via upload
AlekseiVambol Jan 17, 2023
b0045c5
Add files via upload
AlekseiVambol Jan 18, 2023
0678ec5
Update lib.rs
AlekseiVambol Jan 18, 2023
23ed14d
Add files via upload
AlekseiVambol Jan 31, 2023
7c82788
Delete main.rs
AlekseiVambol Feb 7, 2023
8d82e42
Add files via upload
AlekseiVambol Feb 7, 2023
4b4f8bb
Delete main.rs
AlekseiVambol Feb 22, 2023
a012bcf
Rename zkevm-circuits/src/blake2b/Cargo.toml to zkevm-circuits/src/bl…
AlekseiVambol Feb 22, 2023
c9150c3
Rename zkevm-circuits/src/blake2b/src/lib.rs to zkevm-circuits/src/bl…
AlekseiVambol Feb 22, 2023
43305b9
Rename zkevm-circuits/src/blake2b/circuit/Cargo.toml to zkevm-circuit…
AlekseiVambol Feb 22, 2023
2c2bead
Rename zkevm-circuits/src/blake2b/tests/integration.rs to zkevm-circu…
AlekseiVambol Feb 22, 2023
1481510
Add files via upload
AlekseiVambol Feb 22, 2023
587964e
Delete lib.rs
AlekseiVambol May 11, 2023
ba48da2
Add files via upload
AlekseiVambol May 11, 2023
bc9527b
Add files via upload
AlekseiVambol May 11, 2023
f8197a1
Add files via upload
AlekseiVambol Jun 5, 2023
b3959f4
Add files via upload
AlekseiVambol Jun 5, 2023
92e15c3
Add files via upload
AlekseiVambol Jun 5, 2023
06a42ca
Add files via upload
AlekseiVambol Jun 12, 2023
0f6d4ce
Delete Stats.ods
AlekseiVambol Jun 22, 2023
de96f71
Add files via upload
AlekseiVambol Jun 22, 2023
edde7c6
Add files via upload
AlekseiVambol Jun 22, 2023
e7b5b7d
Add files via upload
AlekseiVambol Jun 22, 2023
5090ee8
Add files via upload
AlekseiVambol Jun 22, 2023
394ef0f
Update zkevm-circuits/src/blake2b/circuit/Cargo.toml
AlekseiVambol Jul 10, 2023
b946c7e
Update zkevm-circuits/src/blake2b/circuit/Cargo.toml
AlekseiVambol Jul 10, 2023
0a5185c
Update lib.rs
AlekseiVambol Jul 17, 2023
8d62c17
Delete zkevm-circuits/src/blake2b/hash directory
AlekseiVambol Jul 28, 2023
b083887
Delete zkevm-circuits/src/blake2b/circuit directory
AlekseiVambol Jul 28, 2023
34e0961
Updated BLAKE2b compression function circuit
AlekseiVambol Jul 28, 2023
e2dd84c
Update blake2b.rs
AlekseiVambol Jul 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
616 changes: 616 additions & 0 deletions zkevm-circuits/src/blake2b/circuit/Cargo.lock

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions zkevm-circuits/src/blake2b/circuit/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "blake2b_circuit"
vlopes11 marked this conversation as resolved.
Show resolved Hide resolved
version = "0.1.0"
edition = "2021"


[dependencies]
halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_02_02" }
ark-std = { version = "0.3", features = ["print-trace"] }
rand_xorshift = "0.3"
hex = "0.4"
rand = "0.8"
1,855 changes: 1,855 additions & 0 deletions zkevm-circuits/src/blake2b/circuit/src/lib.rs

Large diffs are not rendered by default.

99 changes: 99 additions & 0 deletions zkevm-circuits/src/blake2b/circuit/tests/bench.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;
use ark_std::{ start_timer, end_timer };
use halo2_proofs:: {
plonk::{ create_proof, keygen_vk, keygen_pk, verify_proof },
poly::kzg::{
commitment::{ KZGCommitmentScheme, ParamsKZG },
multiopen::{ ProverSHPLONK, VerifierSHPLONK },
strategy::SingleStrategy
},
poly::commitment::ParamsProver,
halo2curves::bn256::{ Bn256, Fr, G1Affine },
transcript::{ Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer }
};
use blake2b_circuit::{ CompressionCircuit, CompressionInput };

// Binary logarithm of the height of a circuit instance
const K: u32 = 18;
// Number of rows per round
const R: usize = 8;
// BLAKE2b compression function inputs. Their total round number is the maximum one for
// the chosen k, number of rows per round and amount of the compression function inputs
const INPUTS:[CompressionInput; 5] = [
CompressionInput {
r: 32600,
h: [534542, 235, 325, 235, 53252, 532452, 235324, 25423],
m: [5542, 23, 35, 35, 5252, 52452, 2324, 2523, 254, 35, 354, 235, 5532, 5235, 35, 525],
t: 1234,
f: true,
},
CompressionInput {
r: 13,
h: [532, 235, 325, 235, 53252, 5324654452, 235324, 25423],
m: [55142, 23, 35, 31115, 5252, 52452, 2324, 2523, 254, 35, 354, 235, 5532, 5235, 35, 525],
t: 123784,
f: false,
},
CompressionInput {
r: 90,
h: [532, 235, 325, 235, 53252, 0, 235324, 25423],
m: [55142, 0, 35, 31115, 5252, 52452, 2324, 2523, 254, 35, 354, 235, 5532, 0, 35, 525],
t: 0,
f: true,
},
CompressionInput {
r: 0,
h: [53200, 235, 325, 235, 53252, 0, 235324, 25423],
m: [55142, 0, 35, 31115, 5252, 52452, 232400, 2523, 254, 35, 354, 235, 5532, 0, 350, 52500],
t: 5345435,
f: true
},
CompressionInput {
r: 51,
h: [53200, 235, 325, 235, 53252, 0, 235324, 25423],
m: [55142, 0, 35, 31115, 5252, 52452, 232400, 2523, 254, 35, 354, 235, 5532, 0, 350, 52500],
t: 5345435,
f: true,
}
];

// Runs the test bench for the BLAKE2b compression function circuit
#[test]
#[ignore]
fn bench_circuit() {
println!("The test bench for the BLAKE2b compression function circuit:");

let mut more = INPUTS;
more[0].r += 1;
assert!(CompressionCircuit::<Fr,R>::k(&more) > K,
"The total round number must be the maximum one for the chosen k, number of rows per round and amount of the compression function inputs!");

let circuit = CompressionCircuit::<Fr,R>::new(K, &INPUTS);

let timer = start_timer!(|| "KZG setup");
let mut random = XorShiftRng::from_seed([0xC; 16]);
let general_kzg_params = ParamsKZG::<Bn256>::setup(K, &mut random);
let verifier_kzg_params = general_kzg_params.verifier_params().clone();
end_timer!(timer);

let verifying_key = keygen_vk(&general_kzg_params, &circuit).expect("The verifying key must be generated successfully!");
let proving_key = keygen_pk(&general_kzg_params, verifying_key, &circuit).expect("The proving key must be generated successfully!");
let mut transcript = Blake2bWrite::<Vec<u8>, G1Affine, Challenge255<_>>::init(vec![]);

let timer = start_timer!(|| "Proof generation");
create_proof::<KZGCommitmentScheme<Bn256>, ProverSHPLONK<'_, Bn256>, _, _, _, _>(&general_kzg_params,
&proving_key, &[circuit], &[&[]], random, &mut transcript).expect("The proof must be generated successfully!");
let transcripted = transcript.finalize();
end_timer!(timer);

let performance = 1000 * INPUTS.iter().fold(0, |sum, input| sum + input.r) as u128 / timer.time.elapsed().as_millis();
println!("The prover's performace is {} rounds/second", performance);

let timer = start_timer!(|| "Proof verification");
let mut transcript = Blake2bRead::<_, G1Affine, Challenge255<_>>::init(&transcripted[..]);
let strategy = SingleStrategy::new(&general_kzg_params);
verify_proof::<KZGCommitmentScheme<Bn256>, VerifierSHPLONK<'_, Bn256>, _, _, _>(&verifier_kzg_params,
proving_key.get_vk(), strategy, &[&[]], &mut transcript).expect("The proof must be verified successfully!");
end_timer!(timer);
}
73 changes: 73 additions & 0 deletions zkevm-circuits/src/blake2b/circuit/tests/integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use hex::{ decode_to_slice };
use std::{ array, convert::TryInto };
use halo2_proofs::{ halo2curves::bn256::Fr, dev::MockProver };
use blake2b_circuit::{ CompressionCircuit, CompressionInput, blake2b };

// EIP-152 test vectors 4-7 describing the BLAKE2b compression function inputs with the corresponding outputs. The vectors 1-3
// do not describe the correct inputs and are not representable in the format used in the tested library. The number of rounds
// for the vector 8 is 2^32 - 1, so the corresponding input cannot be processed by a circuit instance
const EIP152_VECTORS: [[&str; 2]; 4] = [
["0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
"08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b"],
["0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
"ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"],
["0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000",
"75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735"],
["0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
"b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421"]
];

// Creates the description of a BLAKE2b compression function input, which corresponds to the
// specified EIP-152 test vector, in accordance with the format used in the tested library
pub fn hex_to_input(v: &str) -> CompressionInput {
let mut buffer = [0u8; 213];
decode_to_slice(v, &mut buffer).expect("Hex input must be correct!");
assert!(buffer[212] <= 1, "Incorrect final block indicator flag!");

CompressionInput {
r: u32::from_be_bytes(buffer[0..4].try_into().unwrap()),
h: array::from_fn(|i| u64::from_le_bytes(buffer[4 + 8 * i..][..8].try_into().unwrap())),
m: array::from_fn(|i| u64::from_le_bytes(buffer[68 + 8 * i..][..8].try_into().unwrap())),
t: u128::from_le_bytes(buffer[196..212].try_into().unwrap()),
f: buffer[212] == 1
}
}

// Tests the circuit instance, which has height 2^17 and processes the BLAKE2b
// compression function inputs corresponding to the EIP-152 test vectors 4-7
#[test]
fn circuit_check() {
let k = 17;
let vectors: Vec<CompressionInput> = EIP152_VECTORS.iter().map(|v| hex_to_input(v[0])).collect();
let circuit = CompressionCircuit::<Fr,128>::new(k, &vectors);
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
prover.assert_satisfied();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm missing the part of this test that we assert the output equals the expected test vector output. Did I overlook something?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we cannot obtain the output directly from the MockProver, we have to check it in a different way. The witnesses generated as the result of circuit synthesis are compared with the results computed by the function not used for witness generation:
// Checking the correctness of the computation
let (h_ex, rlc_ex) = RLCTable::compress_with_rlc(challenge, &input.h, &input.m, input.t, input.f, input.r);
let correctness = (h == h_ex) && (format!("{:?}", rlc) == format!("{:?}", rlc_ex));
assert!(correctness, "Processing of a BLAKE2b compression function input was incorrect! This input is {:?}", input);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is a suggestion of a possible solution.

Given the blake2b circuit takes two advice inputs/witnesses (besides, ofc, the internal ones): the input of the hash, and its output.

First we fetch the input and output from the advice
$$(input, output) \gets \mathbb{A}$$

Then we compute the blake hash into result as the circuit synthesize
$$result \gets \mathbb{C}$$

Finally, we assert the computed result to be the expected witness
$$result \equiv output$$

This approach forces the prover to compute the expected output himself, and allows the users of the circuit to pass this output advice column to different circuits. With that, we can easily pass the expected input/output from the test vectors

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we do not need this, because in the current architecture the supercircuit will compute input/output pair (outside the circuit) during the interaction and hash them by means of random linear combination hash. The input/output pairs and their rlc hashes are also computed by the current circuit (inside it). The rlc hash computed by the supercircuit is going to be constrained to be into the lookup table containing all rlc's computed by the current circuit. Thus, if some i/o pair produced by this circuit is incorrect, then lookup constraint will not be satisfied.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood; however, it is very expensive to test the super circuit. In case a bug is introduced here, it is preferable to test the blake circuit individually rather than depending on the full integrated test.

This is ofc not a blocker, so I'll open an issue to add a unit test in the future

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do no need to test it in the supercircuit. The current test are sufficient. If MockProver doesn't panic during performing the test, this means that the computations performed during the circuit synthesis produce the expected output and all witness data satisfy the constraints. The correctness of the algorithm computing the expected compression function (outside the circuit) is checked by its own test.


// Tests the implementation of the BLAKE2b compression function. The inputs
// with the corresponding outputs are described by the EIP-152 test vectors 4-7
#[test]
fn compression_check() {
let mut expected = [0u8; 64];
for vector in &EIP152_VECTORS {
let input = hex_to_input(vector[0]);
let result = blake2b::compress(input.r, &input.h, &input.m, input.t, input.f);
decode_to_slice(vector[1], &mut expected).expect("Hex expected value must be correct!");
let expected: [u64; 8] = array::from_fn(|i| u64::from_le_bytes(expected[8 * i..][..8].try_into().unwrap()));
assert_eq!(result, expected);
}
}

// Tests the implementation of the "optimums" function,
// which associated with the CompressionCircuit struct
#[test]
fn optimums_check() {
let inputs = [
CompressionInput { r: 200, h: [0; 8], m: [1; 16], t: 2, f: true },
CompressionInput { r: 9000, h: [1; 8], m: [3; 16], t: 1, f: true },
CompressionInput { r: 10000, h: [2; 8], m: [6; 16], t: 0, f: true }];
let optimums = CompressionCircuit::<Fr, 8>::optimums(&inputs);
assert_eq!(optimums[0..18], [None; 18]);
assert_eq!(optimums[18..22], [Some(8), Some(24), Some(48), Some(88)]);
assert_eq!(optimums[22..32], [Some(128); 10]);
}
9 changes: 9 additions & 0 deletions zkevm-circuits/src/blake2b/hash/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "blake2b"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
hex = "0.4.3"
170 changes: 170 additions & 0 deletions zkevm-circuits/src/blake2b/hash/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// Safe Rust implementation of the BLAKE2b hash function (as "digest") in accordance
// with RFC-7693 [1] and BLAKE2b-512 compression function (as "contract") described in
// EIP-152 [2]. The API and implementation details can be understood using only [1] for
// all functions except for "contract". Understanding this one also requires using [2].

// References
// [1] https://www.rfc-editor.org/rfc/rfc7693
// [2] https://eips.ethereum.org/EIPS/eip-152

use core::panic;

const IV: [u64; 8] = [0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, 0x510e527fade682d1,
0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179];

const SIGMA:[[usize; 16]; 10] = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0]];

fn g(v: &mut [u64; 16], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) {
vlopes11 marked this conversation as resolved.
Show resolved Hide resolved
v[a] = v[a].wrapping_add(v[b]).wrapping_add(x);
v[d] = (v[d] ^ v[a]).rotate_right(32);

v[c] = v[c].wrapping_add(v[d]);
v[b] = (v[b] ^ v[c]).rotate_right(24);

v[a] = v[a].wrapping_add(v[b]).wrapping_add(y);
v[d] = (v[d] ^ v[a]).rotate_right(16);

v[c] = v[c].wrapping_add(v[d]);
v[b] = (v[b] ^ v[c]).rotate_right(63);
}

fn compress(h: &mut [u64; 8], m: &[u64; 16], t: u128, f: bool, r: u32) {
let mut v = [0u64; 16];

for i in 0..8 {
v[i] = h[i];
v[i + 8] = IV[i];
}

v[12] ^= t as u64;
v[13] ^= (t >> 64) as u64;

if f { v[14] ^= 0xFFFF_FFFF_FFFF_FFFF; }

for i in 0..(r as usize) {
let s = &SIGMA[i % 10];

g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]);

g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
}

for i in 0..8 {
h[i] ^= v[i] ^ v[i + 8];
}
}

fn load_bytes(d: &mut [u8], s: &[u64]) {
let q = d.len() / 8;
for i in 0..q {
d[8 * i..][..8].copy_from_slice(&s[i].to_le_bytes());
}

let b = d.len() - 8 * q;
if b == 0 { return; }

let mut l = s[q];
for i in 8 * q..d.len() {
d[i] = l as u8;
l >>= 8;
}
}

fn save_bytes(d: &mut [u64], s: &[u8]) {
let q = s.len() / 8;
for i in 0..q {
d[i] = u64::from_le_bytes(s[8 * i..][..8].try_into().unwrap());
}

let b = s.len() - 8 * q;
if b == 0 { return; }

let (mut l, mut p) = (0u64, 0);
for i in 8 * q..s.len() {
l |= (s[i] as u64) << p;
p += 8;
}

d[q] = d[q] & (0xFFFF_FFFF_FFFF_FFFF << (8 * b)) | l;
}

pub fn digest(hash :&mut [u8], msg: &[u8], key: &[u8]) {
if (hash.len() == 0) || (hash.len() > 64) {
panic!("Hash cannot be empty or longer than 64 bytes!");
}
if key.len() > 64 {
panic!("Key cannot be longer than 64 bytes!");
vlopes11 marked this conversation as resolved.
Show resolved Hide resolved
}

const R: u32 = 12;
let mut h = IV;

h[0] ^= 0x0101_0000 ^ ((key.len() as u64) << 8) ^ (hash.len() as u64);

let with_key = key.len() > 0;
let no_msg = msg.len() == 0;
let mut b = [0u64; 16];
let mut t = 0u128;

if with_key {
save_bytes(&mut b, key);
t += 128;
}

if with_key || no_msg {
compress(&mut h, &b, t, no_msg, R);
}

if !no_msg {
let mut n = msg.len() / 128;
if msg.len() % 128 == 0 { n -= 1; }

for i in 0..n {
t += 128;
save_bytes(&mut b, &msg[128 * i..][..128]);
compress(&mut h, &b, t, false, R);
}

b = [0u64; 16];
t += (msg.len() - 128 * n) as u128;
save_bytes(&mut b, &msg[128 * n..]);
compress(&mut h, &b, t, true, R);
}

load_bytes(hash, &h);
}

pub fn contract(output: &mut [u8; 64], input: &[u8; 213]) {
if input[212] > 1 {
panic!("Incorrect final block indicator flag!");
}

let mut h = [0u64; 8];
save_bytes(&mut h, &input[4..68]);
let mut m = [0u64; 16];
save_bytes(&mut m, &input[68..196]);

let t = u128::from_le_bytes(input[196..212].try_into().unwrap());
let f = input[212] == 1;
let r = u32::from_be_bytes(input[0..4].try_into().unwrap());

compress(&mut h, &m, t, f, r);
load_bytes(output, &h);
}
Loading