Skip to content

Commit

Permalink
feat: generalize Hasher interface
Browse files Browse the repository at this point in the history
  • Loading branch information
twoeths committed May 25, 2024
1 parent 650cda8 commit 3cf753d
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 32 deletions.
25 changes: 23 additions & 2 deletions packages/persistent-merkle-tree/src/hasher/as-sha256.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
import {digest2Bytes32, digest64HashObjects, batchHash4HashObjectInputs} from "@chainsafe/as-sha256";
import {digest2Bytes32, digest64HashObjects, HashObject, batchHash4HashObjectInputs} from "@chainsafe/as-sha256";
import type {Hasher} from "./types";

export const hasher: Hasher = {
digest64: digest2Bytes32,
digest64HashObjects,
batchHash4HashObjectInputs,
batchHashObjects: (inputs: HashObject[]) => {
// as-sha256 uses SIMD for batch hash
if (inputs.length === 0) {
return [];
} else if (inputs.length % 2 !== 0) {
throw new Error(`Expect inputs.length to be even, got ${inputs.length}`);
}

const batch = Math.floor(inputs.length / 8);
const outputs = new Array<HashObject>();
for (let i = 0; i < batch; i++) {
const [out0, out1, out2, out3] = batchHash4HashObjectInputs(inputs.slice(i * 8, i * 8 + 8));
outputs.push(out0, out1, out2, out3);
}

for (let i = batch * 8; i < inputs.length; i += 2) {
const output = digest64HashObjects(inputs[i], inputs[i + 1]);
outputs.push(output);
}

return outputs;
},
};
17 changes: 15 additions & 2 deletions packages/persistent-merkle-tree/src/hasher/noble.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {sha256} from "@noble/hashes/sha256";
import {digest64HashObjects, HashObject} from "@chainsafe/as-sha256";
import type {Hasher} from "./types";
import {hashObjectToUint8Array, uint8ArrayToHashObject} from "./util";

Expand All @@ -7,7 +8,19 @@ const digest64 = (a: Uint8Array, b: Uint8Array): Uint8Array => sha256.create().u
export const hasher: Hasher = {
digest64,
digest64HashObjects: (a, b) => uint8ArrayToHashObject(digest64(hashObjectToUint8Array(a), hashObjectToUint8Array(b))),
batchHash4HashObjectInputs: () => {
throw Error("TODO: not implemented");
batchHashObjects: (inputs: HashObject[]) => {
// noble does not support batch hash
if (inputs.length === 0) {
return [];
} else if (inputs.length % 2 !== 0) {
throw new Error(`Expect inputs.length to be even, got ${inputs.length}`);
}

const outputs = new Array<HashObject>();
for (let i = 0; i < inputs.length; i += 2) {
const output = digest64HashObjects(inputs[i], inputs[i + 1]);
outputs.push(output);
}
return outputs;
},
};
5 changes: 4 additions & 1 deletion packages/persistent-merkle-tree/src/hasher/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@ export type Hasher = {
* Hash two 32-byte HashObjects
*/
digest64HashObjects(a: HashObject, b: HashObject): HashObject;
batchHash4HashObjectInputs(inputs: HashObject[]): HashObject[];
/**
* Batch hash 2 * n HashObjects, return n HashObjects output
*/
batchHashObjects(inputs: HashObject[]): HashObject[];
};
38 changes: 11 additions & 27 deletions packages/persistent-merkle-tree/src/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,34 +407,18 @@ export function executeHashComputations(hashComputations: Array<HashComputation[
throw Error(`no hash computations for level ${level}`);
}
// HashComputations of the same level are safe to batch
const batch = Math.floor(hcArr.length / 4);
for (let i = 0; i < batch; i++) {
const item0 = hcArr[i * 4];
const item1 = hcArr[i * 4 + 1];
const item2 = hcArr[i * 4 + 2];
const item3 = hcArr[i * 4 + 3];

const [dest0, dest1, dest2, dest3] = hasher.batchHash4HashObjectInputs([
item0.src0,
item0.src1,
item1.src0,
item1.src1,
item2.src0,
item2.src1,
item3.src0,
item3.src1,
]);

item0.dest.applyHash(dest0);
item1.dest.applyHash(dest1);
item2.dest.applyHash(dest2);
item3.dest.applyHash(dest3);
const inputs: HashObject[] = [];
const dests: Node[] = [];
for (const {src0, src1, dest} of hcArr) {
inputs.push(src0, src1);
dests.push(dest);
}
// compute remaining separatedly
const remLen = hcArr.length % 4;
for (let i = remLen - 1; i >= 0; i--) {
const {src0, src1, dest} = hcArr[hcArr.length - i - 1];
dest.applyHash(hasher.digest64HashObjects(src0, src1));
const outputs = hasher.batchHashObjects(inputs);
if (outputs.length !== dests.length) {
throw Error(`${inputs.length} inputs produce ${outputs.length} outputs, expected ${dests.length} outputs`);
}
for (let i = 0; i < outputs.length; i++) {
dests[i].applyHash(outputs[i]);
}
}
}
Expand Down

0 comments on commit 3cf753d

Please sign in to comment.