Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: batch hash tree root #378

Draft
wants to merge 118 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 103 commits
Commits
Show all changes
118 commits
Select commit Hold shift + click to select a range
2380430
feat: initial implementation of batch hash
twoeths Apr 15, 2024
48854ed
feat: track HashComputation in setNodesAtDepth()
twoeths Apr 25, 2024
6820252
fix: findDiffDepthi to support more than 31 bytes
twoeths Apr 26, 2024
c370e3e
fix: test setNodesAtDepth get computation for > 32 bits
twoeths Apr 26, 2024
b65b06a
feat: implement getHashComputations and executeHashComputations
twoeths May 23, 2024
086daf9
fix: setNodesAtDepth() with HashComputationGroup
twoeths May 23, 2024
b82e718
feat: implement ViewDU.hashTreeRoot() using batch hash
twoeths May 24, 2024
75a4422
fix: precompute hash for zero nodes
twoeths May 24, 2024
008cf3d
chore: add eth2 BeaconState test
twoeths May 25, 2024
6133535
fix: merge issue
twoeths May 25, 2024
e60101a
feat: generalize Hasher interface
twoeths May 25, 2024
b1a7765
chore: add batchHash() benchmark
twoeths May 25, 2024
e14e24a
fix: getHashComputations() unit tests and perf test batchHash()
twoeths May 26, 2024
87094b0
feat: hasher.executeHashComputations()
twoeths May 26, 2024
525bb1f
fix: executeHashComputations() util to call hasher implementation
twoeths May 26, 2024
cef8fa7
chore: add getHashComputations() benchmark
twoeths May 26, 2024
88fd6e8
fix: improve for loop in as-sha256 executeHashComputations
twoeths May 28, 2024
2d5fe10
chore: rename variables
twoeths May 28, 2024
1689afb
fix: improve executeHashComputations() by reducing one if
twoeths Jun 3, 2024
a49d32b
feat: add hashtree hasher
matthewkeil Jun 7, 2024
92d33f0
feat: use hashtreeHasher
matthewkeil Jun 7, 2024
4d40df3
fix: rough out of hashtreeHasher
matthewkeil Jun 7, 2024
f56a6c5
feat: add name to hasher for type detection in tests
matthewkeil Jun 10, 2024
b9f9426
feat: fix bug in hashtree and add batch hashing
matthewkeil Jun 10, 2024
cc57d3d
test: add expectHex util
matthewkeil Jun 10, 2024
94b2d09
test: add unit tests for hashers
matthewkeil Jun 10, 2024
29bdd2a
feat: add null-loader to simpleserialze webpack config
matthewkeil Jun 10, 2024
578f706
feat: add get hashComputations to BranchNode
matthewkeil Jun 11, 2024
39cb5e4
test: add buildComparisonTrees to test utils
matthewkeil Jun 11, 2024
3b8050e
fix: broken perf test
matthewkeil Jun 11, 2024
3ebb4c2
test: add perf comparison for hashers
matthewkeil Jun 11, 2024
bef3d5b
fix: benchmark command in persistent-merkle-tree
matthewkeil Jun 11, 2024
d4293f0
feat: improve hashtree performance a bit
matthewkeil Jun 11, 2024
483f85b
test: add batchHash perf comparison
matthewkeil Jun 11, 2024
6fff41f
chore: add partially modified tree benchmark
twoeths Jun 9, 2024
5ba002c
feat: optimize ContainerNodeStruct.valueToTree()
twoeths Jun 10, 2024
a3a17a0
fix: add batchHash vs hashTreeRoot benchmark
twoeths Jun 10, 2024
a969608
feat: benchmark hash step of each batch hash method
twoeths Jun 10, 2024
396cebe
feat: compute HashComputations when creating validator tree
twoeths Jun 12, 2024
ec89ce9
fix: use hashtree for ssz benchmark
twoeths Jun 12, 2024
07e4d3c
fix: hasher executeHashComputations() benchmark
twoeths Jun 12, 2024
31cec22
fix: allocate memory once for hashtree
twoeths Jun 13, 2024
fbf59ea
fix: improve hashtree.batchHashObjects()
twoeths Jun 13, 2024
c22f368
fix: stablize hasher.test.ts benchmark
twoeths Jun 13, 2024
46ddb4c
feat: use Uint32Array
twoeths Jun 13, 2024
a4d8677
fix: remove binding devDependency
matthewkeil Jun 14, 2024
1a1271e
chore: update yarn.lock
matthewkeil Jun 14, 2024
5cc208b
fix: release backed tree after use in BranchNodeStruct
twoeths Jun 19, 2024
c2f4dc0
feat: sync ValidatorNodeStructType from lodestar
twoeths Jun 19, 2024
b518321
feat: implement ValidatorViewDU class
twoeths Jun 20, 2024
f991f52
feat: implement ListCompositeTreeViewDU
twoeths Jun 20, 2024
0c8ac39
feat: implement and use hashtree.digestNLevelUnsafe()
twoeths Jun 21, 2024
724c057
chore: export more classes
twoeths Jun 22, 2024
7b20306
fix: spec tests
twoeths Jun 23, 2024
ac3a930
feat: ContainerNodeStruct does not support batch hash
twoeths Jun 24, 2024
34d822f
fix: allocate memory once for ListValidatorTreeViewDU
twoeths Jun 24, 2024
0f06a77
fix: implement ValidatorTreeViewDU.commit()
twoeths Jun 24, 2024
f1a37d1
fix: revert valueToTree()
twoeths Jun 24, 2024
dc3f9f2
fix: composite type setters in validator ViewDU
twoeths Jun 24, 2024
58caa51
chore: remove unused code in Validator ViewDU
twoeths Jun 25, 2024
ef6101f
fix: no need to create new validator node in commitToHashObject()
twoeths Jun 25, 2024
829b644
fix: remove getHashComputations() in ListValidator ViewDU
twoeths Jun 25, 2024
6485e14
fix: avoid memory allocation in hashtree
twoeths Jun 29, 2024
e181ded
feat: support digestNLevelUnsafe using as-sha256
twoeths Jun 26, 2024
158601a
fix: use hashtree in setHasher.mjs for test
twoeths Jun 29, 2024
4037ffe
feat: do not allocate temp HashObjects
twoeths Jun 30, 2024
b41b28e
chore: benchmark validator hashTreeRoot time
twoeths Jul 1, 2024
b68f729
fix: lint
twoeths Jul 1, 2024
80d3de2
feat: implement merkleizeInto() for as-sha256 and hashtree
twoeths Jul 2, 2024
f1b5483
fix: deduplicate merkleizeInto() implementations
twoeths Jul 3, 2024
743b7cf
fix: handle empty data in merkleizeInto()
twoeths Jul 4, 2024
0e06479
feat: type.hashTreeRootInto() using hasher merkleizeInto()
twoeths Jul 4, 2024
6c8e27f
chore: benchmark BeaconBlock hashTreeRoot()
twoeths Jul 4, 2024
49846bf
fix: correct cached root
twoeths Jul 4, 2024
1001833
fix: improve digestNLevel to hash to input data
twoeths Jul 4, 2024
59a1221
feat: support offset in byteArrayIntoHashObject
twoeths Jul 4, 2024
e591866
feat: ssz v0.17.0
twoeths Jul 5, 2024
9779b0a
fix: only overwrite ListValidatorViewDU
twoeths Jul 6, 2024
30976f1
fix: publish ContainerNodeStructTreeViewDU
twoeths Jul 6, 2024
c939ee7
fix: preallocate HashComputationsGroup
twoeths Jul 7, 2024
805b350
chore: benchmark merkleize() vs merkleizeInto()
twoeths Jul 9, 2024
fc0531d
fix: do not rebind nodes if child ViewDU is not changed (#380)
twoeths Jul 11, 2024
33e2416
fix: optimize commit() for Container and ArrayComposite ViewDUs (#381)
twoeths Jul 11, 2024
4873a66
feat: support optional output arrays for getAll() apis (#383)
twoeths Jul 15, 2024
dcda46f
fix: merge master
twoeths Jul 19, 2024
7e0aa16
fix: merge issue
twoeths Jul 19, 2024
b6f18fb
feat: minimal memory allocation on ViewDU hashTreeRoot (#388)
twoeths Jul 23, 2024
04f8a16
feat: ReuseListIterator for getAll() api (#390)
twoeths Jul 31, 2024
2b07892
Merge branch 'master' into te/batch_hash_tree_root
twoeths Aug 7, 2024
23557d7
chore: fix lint
twoeths Aug 7, 2024
aa7ebfb
feat: new batchHashTreeRoot() method for ViewDU
twoeths Aug 7, 2024
a6191d4
feat: implement ViewDU.batchHashTreeRoot()
twoeths Aug 9, 2024
6aaa7bd
chore: add benchmark
twoeths Aug 9, 2024
c616b55
feat: implement ViewDU.batchHashTreeRoot, merge PR #392
twoeths Aug 9, 2024
0ee377b
chore: remove describe.only
twoeths Aug 9, 2024
4af75e6
Merge remote-tracking branch 'origin/te/viewdu_batch_hash_tree_root' …
twoeths Aug 9, 2024
04ed553
fix: do not change hashTreeRoot() (#393)
twoeths Aug 16, 2024
ad686e6
fix: handle unmodified validators (#397)
twoeths Aug 22, 2024
0b3ba85
fix: separate batch hash and commit logic in listValidator ViewDU
twoeths Aug 26, 2024
ed1b08a
fix: do not throw error if commit ViewDU without tracking changes
twoeths Aug 26, 2024
1b69676
feat: implement forEach() and forEachValue() for ArrayCompositeType
twoeths Sep 11, 2024
91c735c
feat: ListUintNum64Type create ViewDU from existing tree (#402)
twoeths Sep 13, 2024
fd4112a
chore: merge master
twoeths Sep 13, 2024
7b1b3ad
chore: merge master
twoeths Oct 11, 2024
806c4de
fix: implement getChunkBytes() for StableContainer & Profile
twoeths Oct 11, 2024
1da53b3
fix: lint
twoeths Oct 11, 2024
024538d
feat: use allocUnsafe() for hashTreeRoot()
twoeths Oct 11, 2024
95cc29e
chore: revert unnecessary change to master
twoeths Oct 14, 2024
9315119
chore: remove ListIterator
twoeths Oct 14, 2024
f17019e
fix: dedup executeHashComputations - hashtree perf test
twoeths Oct 14, 2024
79152c4
fix: no memory allocation in StableContainer Profile hashTreeRootInto()
twoeths Oct 14, 2024
d03c720
chore: revert unnecessary change
twoeths Oct 15, 2024
c7e03b8
fix: cache same root instance from CompositeType.hashTreeRoot()
twoeths Oct 15, 2024
e0e3173
feat: hashtree as default hasher
twoeths Oct 18, 2024
4f7a0a4
Revert "feat: hashtree as default hasher"
twoeths Oct 31, 2024
3249906
refactor: chunkBytesBuffer to blocksBuffer to reflect SHA256 blocks
twoeths Oct 31, 2024
025a069
refactor: merkleizeInto to merkleizeBlocksBytes
twoeths Oct 31, 2024
fbbf918
feat: merkleize block array (#420)
twoeths Nov 6, 2024
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
11 changes: 9 additions & 2 deletions packages/persistent-merkle-tree/src/packedNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@ export function packedRootsBytesToNode(depth: number, dataView: DataView, start:
*
* h0 h1 h2 h3 h4 h5 h6 h7
* |------|------|------|------|------|------|------|------|
*
* @param values list of uint64 numbers
* @param leafNodes optional list of LeafNodes to reuse
*/
export function packedUintNum64sToLeafNodes(values: number[]): LeafNode[] {
const leafNodes = new Array<LeafNode>(Math.ceil(values.length / 4));
export function packedUintNum64sToLeafNodes(values: number[], leafNodes?: LeafNode[]): LeafNode[] {
const nodeCount = Math.ceil(values.length / 4);
if (leafNodes && leafNodes.length !== nodeCount) {
throw new Error(`Invalid leafNode length: ${leafNodes.length} !== ${nodeCount}`);
}
leafNodes = leafNodes ?? new Array<LeafNode>(Math.ceil(values.length / 4));
for (let i = 0; i < values.length; i++) {
const nodeIndex = Math.floor(i / 4);
const leafNode = leafNodes[nodeIndex] ?? new LeafNode(0, 0, 0, 0, 0, 0, 0, 0);
Expand Down
4 changes: 2 additions & 2 deletions packages/persistent-merkle-tree/test/perf/hasher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ describe("hashtree", function () {
});

itBench({
id: `executeHashComputations`,
id: `executeHashComputations - hashtree`,
beforeEach: () => {
const [tree] = buildComparisonTrees(16);
return tree;
Expand All @@ -92,7 +92,7 @@ describe("hashtree", function () {
});

itBench({
id: `get root`,
id: `root - hashtree`,
beforeEach: () => {
const [tree] = buildComparisonTrees(16);
setHasher(hashtreeHasher);
Expand Down
2 changes: 1 addition & 1 deletion packages/persistent-merkle-tree/test/perf/node.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {itBench} from "@dapplion/benchmark";
import {BranchNode, getNodeH, LeafNode} from "../../src/node";
import {countToDepth, getHashComputations, HashComputation, subtreeFillToContents} from "../../src";
import {countToDepth, getHashComputations, subtreeFillToContents} from "../../src";
import {batchHash} from "../utils/batchHash";

describe("HashObject LeafNode", () => {
Expand Down
1 change: 1 addition & 0 deletions packages/simpleserialize.com/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"eyzy-tree": "^0.2.2",
"file-saver": "^2.0.5",
"js-yaml": "^4.1.0",
"null-loader": "^4.0.1",
"react": "^17.0.2",
"react-alert": "^7.0.1",
"react-alert-template-basic": "^1.0.0",
Expand Down
86 changes: 47 additions & 39 deletions packages/simpleserialize.com/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
const webpack = require('webpack');
const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require("webpack");
const {resolve} = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

const isProd = process.env.NODE_ENV === 'production';
const isProd = process.env.NODE_ENV === "production";

const config = {
devtool: "source-map",
mode: isProd ? 'production' : 'development',
mode: isProd ? "production" : "development",
entry: {
index: './src/index.tsx',
index: "./src/index.tsx",
},
output: {
path: resolve(__dirname, 'dist'),
filename: '[name].js',
path: resolve(__dirname, "dist"),
filename: "[name].js",
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
extensions: [".js", ".jsx", ".ts", ".tsx"],
},
module: {
rules: [
Expand All @@ -27,68 +27,72 @@ const config = {
},
},
{
test: /\.scss$/,
use: [
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader'
loader: "css-loader",
},
{
loader: 'sass-loader',
loader: "sass-loader",
options: {
sourceMap: true,
}
}
]
},{
},
},
],
},
{
test: /\.tsx?$/,
use: 'babel-loader',
use: "babel-loader",
exclude: /node_modules/,
}
},
{
use: "null-loader",
test: /@chainsafe\/hashtree/,
},
],
},
plugins: [
new webpack.ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer'],
process: "process/browser",
Buffer: ["buffer", "Buffer"],
}),
new MiniCssExtractPlugin({
filename: 'css/[name].bundle.css'
filename: "css/[name].bundle.css",
}),
new HtmlWebpackPlugin({
title: 'Simple Serialize | Chainsafe Systems',
template: 'src/index.html',
title: "Simple Serialize | Chainsafe Systems",
template: "src/index.html",
}),
],
};

if (isProd) {
config.optimization = {
minimizer: [
],
minimizer: [],
};
} else {
config.devServer = {
port: 8080, // https://webpack.js.org/configuration/dev-server/#devserverport
open: true, // https://webpack.js.org/configuration/dev-server/#devserveropen
hot: true, // https://webpack.js.org/configuration/dev-server/#devserverhot
compress: true, // https://webpack.js.org/configuration/dev-server/#devservercompress
stats: 'errors-only', // https://webpack.js.org/configuration/dev-server/#devserverstats-
stats: "errors-only", // https://webpack.js.org/configuration/dev-server/#devserverstats-
overlay: true, // https://webpack.js.org/configuration/dev-server/#devserveroverlay
};
}

const workerConfig = {
name: "worker",
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
extensions: [".js", ".jsx", ".ts", ".tsx"],
},
entry: {
index: './src/components/worker/index.ts',
index: "./src/components/worker/index.ts",
},
output: {
path: resolve(__dirname, 'dist'),
filename: 'worker.js',
path: resolve(__dirname, "dist"),
filename: "worker.js",
},
module: {
rules: [
Expand All @@ -100,21 +104,25 @@ const workerConfig = {
},
{
test: /worker?$/,
loader: 'threads-webpack-plugin',
loader: "threads-webpack-plugin",
},
{
test: /\.ts?$/,
use: 'babel-loader',
use: "babel-loader",
exclude: /node_modules/,
}
},
{
use: "null-loader",
test: /@chainsafe\/hashtree/,
},
],
},
plugins: [
new webpack.ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer'],
process: "process/browser",
Buffer: ["buffer", "Buffer"],
}),
]
}
],
};

module.exports = [config, workerConfig];
10 changes: 9 additions & 1 deletion packages/ssz/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,21 @@ export {Type, ValueOf, JsonPath, ByteViews} from "./type/abstract";
export {BasicType, isBasicType} from "./type/basic";
export {CompositeType, CompositeTypeAny, CompositeView, CompositeViewDU, isCompositeType} from "./type/composite";
export {TreeView} from "./view/abstract";
export {ValueOfFields} from "./view/container";
export {ValueOfFields, ContainerTypeGeneric} from "./view/container";
export {TreeViewDU} from "./viewDU/abstract";
export {ListCompositeTreeViewDU} from "./viewDU/listComposite";
export {ListBasicTreeViewDU} from "./viewDU/listBasic";
export {ArrayCompositeTreeViewDUCache} from "./viewDU/arrayComposite";
export {ContainerNodeStructTreeViewDU} from "./viewDU/containerNodeStruct";

// Values
export {BitArray, getUint8ByteToBitBooleanArray} from "./value/bitArray";

// Utils
export {fromHexString, toHexString, byteArrayEquals} from "./util/byteArray";
export {ReusableListIterator} from "./util/reusableListIterator";

export {hash64, symbolCachedPermanentRoot} from "./util/merkleize";

// others
export {BranchNodeStruct} from "./branchNodeStruct";
6 changes: 6 additions & 0 deletions packages/ssz/src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ export interface List<T> extends ArrayLike<T> {
pop(): T | undefined;
}

export interface ListIterator<T> {
readonly length: number;
push(...values: T[]): void;
[Symbol.iterator](): Iterator<T>;
}

export type Container<T extends Record<string, unknown>> = T;

export type ByteVector = Vector<number>;
Expand Down
5 changes: 5 additions & 0 deletions packages/ssz/src/type/abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ export abstract class Type<V> {
*/
abstract hashTreeRoot(value: V): Uint8Array;

/**
* Same to hashTreeRoot() but here we write result to output.
*/
abstract hashTreeRootInto(value: V, output: Uint8Array, offset: number): void;

// JSON support

/** Parse JSON representation of a type to value */
Expand Down
26 changes: 17 additions & 9 deletions packages/ssz/src/type/arrayComposite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,21 +211,29 @@ export function tree_deserializeFromBytesArrayComposite<ElementType extends Comp
}
}

/**
* @param length In List length = value.length, Vector length = fixed value
*/
export function value_getRootsArrayComposite<ElementType extends CompositeType<unknown, unknown, unknown>>(
export function value_getChunkBytesArrayComposite<ElementType extends CompositeType<unknown, unknown, unknown>>(
elementType: ElementType,
length: number,
value: ValueOf<ElementType>[]
): Uint8Array[] {
const roots = new Array<Uint8Array>(length);
value: ValueOf<ElementType>[],
chunkBytesBuffer: Uint8Array
): Uint8Array {
const isOddChunk = length % 2 === 1;
const chunkBytesLen = isOddChunk ? length * 32 + 32 : length * 32;
if (chunkBytesLen > chunkBytesBuffer.length) {
throw new Error(`chunkBytesBuffer is too small: ${chunkBytesBuffer.length} < ${chunkBytesLen}`);
}
const chunkBytes = chunkBytesBuffer.subarray(0, chunkBytesLen);

for (let i = 0; i < length; i++) {
roots[i] = elementType.hashTreeRoot(value[i]);
elementType.hashTreeRootInto(value[i], chunkBytes, i * 32);
}

if (isOddChunk) {
// similar to append zeroHash(0)
chunkBytes.subarray(length * 32, chunkBytesLen).fill(0);
}

return roots;
return chunkBytes;
}

function readOffsetsArrayComposite(
Expand Down
12 changes: 9 additions & 3 deletions packages/ssz/src/type/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,17 @@ export abstract class BasicType<V> extends Type<V> {
}

hashTreeRoot(value: V): Uint8Array {
// TODO: Optimize
const uint8Array = new Uint8Array(32);
const root = new Uint8Array(32);
twoeths marked this conversation as resolved.
Show resolved Hide resolved
this.hashTreeRootInto(value, root, 0);
return root;
}

hashTreeRootInto(value: V, output: Uint8Array, offset: number): void {
const uint8Array = output.subarray(offset, offset + 32);
// output could have preallocated data, some types may not fill the whole 32 bytes
uint8Array.fill(0);
const dataView = new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength);
this.value_serializeToBytes({uint8Array, dataView}, 0, value);
return uint8Array;
}

clone(value: V): V {
Expand Down
13 changes: 10 additions & 3 deletions packages/ssz/src/type/bitArray.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {concatGindices, Gindex, Node, toGindex, Tree, HashComputationLevel} from "@chainsafe/persistent-merkle-tree";
import {fromHexString, toHexString, byteArrayEquals} from "../util/byteArray";
import {splitIntoRootChunks} from "../util/merkleize";
import {CompositeType, LENGTH_GINDEX} from "./composite";
import {BitArray} from "../value/bitArray";
import {BitArrayTreeView} from "../view/bitArray";
import {BitArrayTreeViewDU} from "../viewDU/bitArray";
import {getChunkBytes} from "./byteArray";

/* eslint-disable @typescript-eslint/member-ordering */

Expand Down Expand Up @@ -40,8 +40,15 @@ export abstract class BitArrayType extends CompositeType<BitArray, BitArrayTreeV

// Merkleization

protected getRoots(value: BitArray): Uint8Array[] {
return splitIntoRootChunks(value.uint8Array);
protected getChunkBytes(value: BitArray): Uint8Array {
// reallocate this.merkleBytes if needed
if (value.uint8Array.length > this.chunkBytesBuffer.length) {
const chunkCount = Math.ceil(value.bitLen / 8 / 32);
const chunkBytes = chunkCount * 32;
// pad 1 chunk if maxChunkCount is not even
this.chunkBytesBuffer = chunkCount % 2 === 1 ? new Uint8Array(chunkBytes + 32) : new Uint8Array(chunkBytes);
twoeths marked this conversation as resolved.
Show resolved Hide resolved
}
return getChunkBytes(value.uint8Array, this.chunkBytesBuffer);
}

// Proofs
Expand Down
29 changes: 26 additions & 3 deletions packages/ssz/src/type/bitList.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import {getNodesAtDepth, Node, packedNodeRootsToBytes, packedRootsBytesToNode} from "@chainsafe/persistent-merkle-tree";
import {mixInLength, maxChunksToDepth} from "../util/merkleize";
import {
getNodesAtDepth,
merkleizeInto,
Node,
packedNodeRootsToBytes,
packedRootsBytesToNode,
} from "@chainsafe/persistent-merkle-tree";
import {maxChunksToDepth} from "../util/merkleize";
import {Require} from "../util/types";
import {namedClass} from "../util/named";
import {ByteViews} from "./composite";
Expand Down Expand Up @@ -29,6 +35,12 @@ export class BitListType extends BitArrayType {
readonly maxSize: number;
readonly maxChunkCount: number;
readonly isList = true;
readonly mixInLengthChunkBytes = new Uint8Array(64);
readonly mixInLengthBuffer = Buffer.from(
this.mixInLengthChunkBytes.buffer,
this.mixInLengthChunkBytes.byteOffset,
this.mixInLengthChunkBytes.byteLength
);

constructor(readonly limitBits: number, opts?: BitListOptions) {
super();
Expand Down Expand Up @@ -101,7 +113,18 @@ export class BitListType extends BitArrayType {
// Merkleization: inherited from BitArrayType

hashTreeRoot(value: BitArray): Uint8Array {
return mixInLength(super.hashTreeRoot(value), value.bitLen);
const root = new Uint8Array(32);
this.hashTreeRootInto(value, root, 0);
twoeths marked this conversation as resolved.
Show resolved Hide resolved
return root;
}

hashTreeRootInto(value: BitArray, output: Uint8Array, offset: number): void {
super.hashTreeRootInto(value, this.mixInLengthChunkBytes, 0);
// mixInLength
this.mixInLengthBuffer.writeUIntLE(value.bitLen, 32, 6);
// one for hashTreeRoot(value), one for length
const chunkCount = 2;
merkleizeInto(this.mixInLengthChunkBytes, chunkCount, output, offset);
}

// Proofs: inherited from BitArrayType
Expand Down
Loading
Loading