Skip to content

Commit fd1dac8

Browse files
authored
feat: use lodestar-bun hashtree (#8481)
**Motivation** - #7280 **Description** - bump lodestar-bun - leveldb uses snappy - leveldb macos build fix - use hashtree-bun hasher in bun
1 parent 51e1ede commit fd1dac8

File tree

7 files changed

+159
-8
lines changed

7 files changed

+159
-8
lines changed

packages/cli/package.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,18 @@
99
},
1010
"homepage": "https://github.com/ChainSafe/lodestar#readme",
1111
"type": "module",
12-
"exports": "./lib/index.js",
12+
"exports": {
13+
".": {
14+
"bun": "./src/index.ts",
15+
"import": "./lib/index.js"
16+
}
17+
},
18+
"imports": {
19+
"#hashtree-hasher": {
20+
"bun": "./src/util/hasher_bun.ts",
21+
"import": "./lib/util/hasher_nodejs.js"
22+
}
23+
},
1324
"files": [
1425
"src",
1526
"lib",
@@ -65,6 +76,7 @@
6576
"@libp2p/peer-id": "^5.1.0",
6677
"@lodestar/api": "^1.34.1",
6778
"@lodestar/beacon-node": "^1.34.1",
79+
"@lodestar/bun": "git+https://github.com/ChainSafe/lodestar-bun.git",
6880
"@lodestar/config": "^1.34.1",
6981
"@lodestar/db": "^1.34.1",
7082
"@lodestar/light-client": "^1.34.1",

packages/cli/src/applyPreset.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// MUST import this file first before anything and not import any Lodestar code.
2+
3+
import {hasher as hashtreeHasher} from "#hashtree-hasher";
24
import {setHasher} from "@chainsafe/persistent-merkle-tree";
3-
import {hasher as hashtreeHasher} from "@chainsafe/persistent-merkle-tree/hasher/hashtree";
45

56
// Without setting this first, persistent-merkle-tree will use noble instead
67
setHasher(hashtreeHasher);
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import {byteArrayIntoHashObject} from "@chainsafe/as-sha256";
2+
import {
3+
HashComputationLevel,
4+
HashObject,
5+
Hasher,
6+
Node,
7+
doDigestNLevel,
8+
doMerkleizeBlockArray,
9+
doMerkleizeBlocksBytes,
10+
} from "@chainsafe/persistent-merkle-tree";
11+
import {digest2Bytes32, digest2Bytes32Into, hashInto} from "@lodestar/bun";
12+
13+
/**
14+
* Best SIMD implementation is in 512 bits = 64 bytes
15+
* If not, hashtree will make a loop inside
16+
* Given sha256 operates on a block of 4 bytes, we can hash 16 inputs at once
17+
* Each input is 64 bytes
18+
*/
19+
const PARALLEL_FACTOR = 16;
20+
const MAX_INPUT_SIZE = PARALLEL_FACTOR * 64;
21+
const uint8Input = new Uint8Array(MAX_INPUT_SIZE);
22+
const uint32Input = new Uint32Array(uint8Input.buffer);
23+
const uint8Output = new Uint8Array(PARALLEL_FACTOR * 32);
24+
// having this will cause more memory to extract uint32
25+
// const uint32Output = new Uint32Array(uint8Output.buffer);
26+
// convenient reusable Uint8Array for hash64
27+
const hash64Input = uint8Input.subarray(0, 64);
28+
const hash64Output = uint8Output.subarray(0, 32);
29+
// size input array to 2 HashObject per computation * 32 bytes per object
30+
const destNodes: Node[] = new Array<Node>(PARALLEL_FACTOR);
31+
32+
export const hasher: Hasher = {
33+
name: "hashtree-bun",
34+
hashInto,
35+
digest64(obj1: Uint8Array, obj2: Uint8Array): Uint8Array {
36+
return digest2Bytes32(obj1, obj2);
37+
},
38+
digest64Into: (obj1: Uint8Array, obj2: Uint8Array, output: Uint8Array): void => {
39+
digest2Bytes32Into(obj1, obj2, output);
40+
},
41+
digest64HashObjects(left: HashObject, right: HashObject, parent: HashObject): void {
42+
hashObjectsToUint32Array(left, right, uint32Input);
43+
hashInto(hash64Input, hash64Output);
44+
byteArrayIntoHashObject(hash64Output, 0, parent);
45+
},
46+
merkleizeBlocksBytes(blocksBytes: Uint8Array, padFor: number, output: Uint8Array, offset: number): void {
47+
doMerkleizeBlocksBytes(blocksBytes, padFor, output, offset, hashInto);
48+
},
49+
merkleizeBlockArray(blocks, blockLimit, padFor, output, offset) {
50+
doMerkleizeBlockArray(blocks, blockLimit, padFor, output, offset, hashInto, uint8Input);
51+
},
52+
digestNLevel(data: Uint8Array, nLevel: number): Uint8Array {
53+
return doDigestNLevel(data, nLevel, hashInto);
54+
},
55+
executeHashComputations(hashComputations: HashComputationLevel[]): void {
56+
for (let level = hashComputations.length - 1; level >= 0; level--) {
57+
const hcArr = hashComputations[level];
58+
if (!hcArr) {
59+
// should not happen
60+
throw Error(`no hash computations for level ${level}`);
61+
}
62+
63+
if (hcArr.length === 0) {
64+
// nothing to hash
65+
continue;
66+
}
67+
68+
// hash every 16 inputs at once to avoid memory allocation
69+
let i = 0;
70+
for (const {src0, src1, dest} of hcArr) {
71+
if (!src0 || !src1 || !dest) {
72+
throw new Error(`Invalid HashComputation at index ${i}`);
73+
}
74+
const indexInBatch = i % PARALLEL_FACTOR;
75+
const offset = indexInBatch * 16;
76+
77+
hashObjectToUint32Array(src0, uint32Input, offset);
78+
hashObjectToUint32Array(src1, uint32Input, offset + 8);
79+
destNodes[indexInBatch] = dest;
80+
if (indexInBatch === PARALLEL_FACTOR - 1) {
81+
hashInto(uint8Input, uint8Output);
82+
for (const [j, destNode] of destNodes.entries()) {
83+
byteArrayIntoHashObject(uint8Output, j * 32, destNode);
84+
}
85+
}
86+
i++;
87+
}
88+
89+
const remaining = hcArr.length % PARALLEL_FACTOR;
90+
// we prepared data in input, now hash the remaining
91+
if (remaining > 0) {
92+
const remainingInput = uint8Input.subarray(0, remaining * 64);
93+
const remainingOutput = uint8Output.subarray(0, remaining * 32);
94+
hashInto(remainingInput, remainingOutput);
95+
// destNodes was prepared above
96+
for (let j = 0; j < remaining; j++) {
97+
byteArrayIntoHashObject(remainingOutput, j * 32, destNodes[j]);
98+
}
99+
}
100+
}
101+
},
102+
};
103+
104+
function hashObjectToUint32Array(obj: HashObject, arr: Uint32Array, offset: number): void {
105+
arr[offset] = obj.h0;
106+
arr[offset + 1] = obj.h1;
107+
arr[offset + 2] = obj.h2;
108+
arr[offset + 3] = obj.h3;
109+
arr[offset + 4] = obj.h4;
110+
arr[offset + 5] = obj.h5;
111+
arr[offset + 6] = obj.h6;
112+
arr[offset + 7] = obj.h7;
113+
}
114+
115+
// note that uint32ArrayToHashObject will cause more memory
116+
function hashObjectsToUint32Array(obj1: HashObject, obj2: HashObject, arr: Uint32Array): void {
117+
arr[0] = obj1.h0;
118+
arr[1] = obj1.h1;
119+
arr[2] = obj1.h2;
120+
arr[3] = obj1.h3;
121+
arr[4] = obj1.h4;
122+
arr[5] = obj1.h5;
123+
arr[6] = obj1.h6;
124+
arr[7] = obj1.h7;
125+
arr[8] = obj2.h0;
126+
arr[9] = obj2.h1;
127+
arr[10] = obj2.h2;
128+
arr[11] = obj2.h3;
129+
arr[12] = obj2.h4;
130+
arr[13] = obj2.h5;
131+
arr[14] = obj2.h6;
132+
arr[15] = obj2.h7;
133+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import {hasher} from "@chainsafe/persistent-merkle-tree/hasher/hashtree";
2+
3+
export {hasher};

packages/cli/tsconfig.build.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"extends": "../../tsconfig.build.json",
33
"include": ["src"],
44
"compilerOptions": {
5+
"rootDir": "src",
56
"outDir": "lib"
67
}
78
}

packages/cli/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"extends": "../../tsconfig.json",
33
"include": ["src", "test"],
44
"compilerOptions": {
5+
"rootDir": "../..",
56
"outDir": "lib"
67
}
78
}

yarn.lock

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -550,10 +550,10 @@
550550
"@chainsafe/blst-linux-x64-musl" "2.2.0"
551551
"@chainsafe/blst-win32-x64-msvc" "2.2.0"
552552

553-
"@chainsafe/bun-ffi-z@^1.1.3":
554-
version "1.1.3"
555-
resolved "https://registry.yarnpkg.com/@chainsafe/bun-ffi-z/-/bun-ffi-z-1.1.3.tgz#152a06a9cde8e0050ac400703ec5c438fcbcd221"
556-
integrity sha512-KlhcVDUYdcAI7KRy4UTQvxMtaFGpSodHizNy/1aHFaC+sjCKBoTTaVdQhbcVF1Jy3O12WK7ZzTlljpKgEvma+Q==
553+
"@chainsafe/bun-ffi-z@^1.1.4":
554+
version "1.1.4"
555+
resolved "https://registry.yarnpkg.com/@chainsafe/bun-ffi-z/-/bun-ffi-z-1.1.4.tgz#7f069f4c66771ca0850d675193be707dbc06922b"
556+
integrity sha512-yegYWjcj9pMwrf554iuFGoUc+Kq8EWDrVpeGiPl/nmXu/lm5EaUzxsiLG6WR1J9tpTQk012213y9RA5PwDrUTQ==
557557

558558
"@chainsafe/discv5@^11.0.4":
559559
version "11.0.4"
@@ -2140,9 +2140,9 @@
21402140

21412141
"@lodestar/bun@git+https://github.com/ChainSafe/lodestar-bun.git":
21422142
version "0.1.0"
2143-
resolved "git+https://github.com/ChainSafe/lodestar-bun.git#368dfa6e112171231d23edbc34e55ad53054442e"
2143+
resolved "git+https://github.com/ChainSafe/lodestar-bun.git#e1b7001df927619284c1a29320240c4834278777"
21442144
dependencies:
2145-
"@chainsafe/bun-ffi-z" "^1.1.3"
2145+
"@chainsafe/bun-ffi-z" "^1.1.4"
21462146

21472147
"@lukeed/ms@^2.0.2":
21482148
version "2.0.2"

0 commit comments

Comments
 (0)