|
1 |
| -import {describe, it, expect} from "vitest" |
| 1 | +import {describe, it, expect} from "vitest"; |
2 | 2 |
|
3 | 3 | import {expectEqualHex} from "../utils/expectHex.js";
|
4 | 4 | import {uint8ArrayToHashObject, hashObjectToUint8Array} from "../../src/hasher/util.js";
|
5 | 5 | import {hasher as nobleHasher} from "../../src/hasher/noble.js";
|
6 | 6 | import {hasher as asSha256Hasher} from "../../src/hasher/as-sha256.js";
|
7 | 7 | import {hasher as hashtreeHasher} from "../../src/hasher/hashtree.js";
|
8 | 8 | import {buildComparisonTrees} from "../utils/tree.js";
|
9 |
| -import {HashComputationLevel, HashObject, LeafNode, getHashComputations, subtreeFillToContents} from "../../src/index.js"; |
10 |
| -import { zeroHash } from "../../src/zeroHash.js"; |
11 |
| - |
12 |
| -const hashers = [hashtreeHasher, asSha256Hasher, nobleHasher]; |
| 9 | +import { |
| 10 | + HashComputationLevel, |
| 11 | + HashObject, |
| 12 | + Hasher, |
| 13 | + LeafNode, |
| 14 | + getHashComputations, |
| 15 | + subtreeFillToContents, |
| 16 | +} from "../../src/index.js"; |
| 17 | +import {zeroHash} from "../../src/zeroHash.js"; |
13 | 18 |
|
14 | 19 | describe("hashers", function () {
|
| 20 | + const hashers: Hasher[] = [hashtreeHasher, asSha256Hasher, nobleHasher]; |
| 21 | + |
| 22 | + before(async function () { |
| 23 | + for (const hasher of hashers) { |
| 24 | + if (typeof hasher.initialize === "function") { |
| 25 | + await hasher.initialize(); |
| 26 | + } |
| 27 | + } |
| 28 | + }); |
| 29 | + |
15 | 30 | describe("digest64 vs digest64HashObjects methods should be the same", () => {
|
16 | 31 | for (const hasher of hashers) {
|
17 | 32 | it(`${hasher.name} hasher`, () => {
|
@@ -68,107 +83,106 @@ describe("hashers", function () {
|
68 | 83 | });
|
69 | 84 | }
|
70 | 85 | });
|
71 |
| -}); |
72 | 86 |
|
73 |
| -describe("hasher.digestNLevel", function () { |
74 |
| - const hashers = [nobleHasher, hashtreeHasher, asSha256Hasher]; |
75 |
| - for (const hasher of hashers) { |
76 |
| - const numValidators = [1, 2, 3, 4]; |
77 |
| - for (const numValidator of numValidators) { |
78 |
| - it(`${hasher.name} digestNLevel ${numValidator} validators = ${8 * numValidator} chunk(s)`, () => { |
79 |
| - const nodes = Array.from({length: 8 * numValidator}, (_, i) => |
80 |
| - LeafNode.fromRoot(Buffer.alloc(32, i + numValidator)) |
81 |
| - ); |
82 |
| - const hashInput = Buffer.concat(nodes.map((node) => node.root)); |
83 |
| - const hashOutput = hasher.digestNLevel(hashInput, 3).slice(); |
84 |
| - for (let i = 0; i < numValidator; i++) { |
85 |
| - const root = subtreeFillToContents(nodes.slice(i * 8, (i + 1) * 8), 3).root; |
86 |
| - expectEqualHex(hashOutput.subarray(i * 32, (i + 1) * 32), root); |
87 |
| - } |
88 |
| - }); |
| 87 | + describe("hasher.digestNLevel", function () { |
| 88 | + for (const hasher of hashers) { |
| 89 | + const numValidators = [1, 2, 3, 4]; |
| 90 | + for (const numValidator of numValidators) { |
| 91 | + it(`${hasher.name} digestNLevel ${numValidator} validators = ${8 * numValidator} chunk(s)`, () => { |
| 92 | + const nodes = Array.from({length: 8 * numValidator}, (_, i) => |
| 93 | + LeafNode.fromRoot(Buffer.alloc(32, i + numValidator)) |
| 94 | + ); |
| 95 | + const hashInput = Buffer.concat(nodes.map((node) => node.root)); |
| 96 | + const hashOutput = hasher.digestNLevel(hashInput, 3).slice(); |
| 97 | + for (let i = 0; i < numValidator; i++) { |
| 98 | + const root = subtreeFillToContents(nodes.slice(i * 8, (i + 1) * 8), 3).root; |
| 99 | + expectEqualHex(hashOutput.subarray(i * 32, (i + 1) * 32), root); |
| 100 | + } |
| 101 | + }); |
| 102 | + } |
89 | 103 | }
|
90 |
| - } |
91 |
| -}); |
92 |
| - |
93 |
| -describe("hasher.merkleizeInto", function () { |
94 |
| - const numNodes = [0, 1, 2, 3, 4, 5, 6, 7, 8]; |
95 |
| - for (const hasher of [nobleHasher, hashtreeHasher, asSha256Hasher]) { |
96 |
| - it(`${hasher.name} should throw error if not multiple of 64 bytes`, () => { |
97 |
| - const data = Buffer.alloc(63, 0); |
98 |
| - const output = Buffer.alloc(32); |
99 |
| - expect(() => hasher.merkleizeBlocksBytes(data, 2, output, 0)).to.throw("Invalid input length"); |
100 |
| - }); |
| 104 | + }); |
101 | 105 |
|
102 |
| - for (const numNode of numNodes) { |
103 |
| - it(`${hasher.name}.merkleizeBlocksBytes for ${numNode} nodes`, () => { |
104 |
| - const nodes = Array.from({length: numNode}, (_, i) => LeafNode.fromRoot(Buffer.alloc(32, i))); |
105 |
| - const data = Buffer.concat(nodes.map((node) => node.root)); |
| 106 | + describe("hasher.merkleizeBlocksBytes", function () { |
| 107 | + const numNodes = [0, 1, 2, 3, 4, 5, 6, 7, 8]; |
| 108 | + for (const hasher of hashers) { |
| 109 | + it(`${hasher.name} should throw error if not multiple of 64 bytes`, () => { |
| 110 | + const data = Buffer.alloc(63, 0); |
106 | 111 | const output = Buffer.alloc(32);
|
107 |
| - const chunkCount = Math.max(numNode, 1); |
108 |
| - const padData = numNode % 2 === 1 ? Buffer.concat([data, zeroHash(0)]) : data; |
109 |
| - hasher.merkleizeBlocksBytes(padData, chunkCount, output, 0); |
110 |
| - const depth = Math.ceil(Math.log2(chunkCount)); |
111 |
| - const root = subtreeFillToContents(nodes, depth).root; |
112 |
| - expectEqualHex(output, root); |
| 112 | + expect(() => hasher.merkleizeBlocksBytes(data, 2, output, 0)).to.throw("Invalid input length"); |
113 | 113 | });
|
114 |
| - } |
115 |
| - } |
116 |
| -}); |
117 | 114 |
|
118 |
| -/** |
119 |
| - * The same to the previous test, but using the merkleizeBlockArray method |
120 |
| - */ |
121 |
| -describe("hasher.merkleizeBlockArray", function () { |
122 |
| - for (const hasher of [nobleHasher, hashtreeHasher, asSha256Hasher]) { |
123 |
| - it(`${hasher.name} should throw error if invalid blockLimit`, () => { |
124 |
| - const data = Buffer.alloc(64, 0); |
125 |
| - const output = Buffer.alloc(32); |
126 |
| - expect(() => hasher.merkleizeBlockArray([data], 2, 2, output, 0)).to.throw( |
127 |
| - "Invalid blockLimit, expect to be less than or equal blocks.length 1, got 2" |
128 |
| - ); |
129 |
| - }); |
| 115 | + for (const numNode of numNodes) { |
| 116 | + it(`${hasher.name}.merkleizeBlocksBytes for ${numNode} nodes`, () => { |
| 117 | + const nodes = Array.from({length: numNode}, (_, i) => LeafNode.fromRoot(Buffer.alloc(32, i))); |
| 118 | + const data = Buffer.concat(nodes.map((node) => node.root)); |
| 119 | + const output = Buffer.alloc(32); |
| 120 | + const chunkCount = Math.max(numNode, 1); |
| 121 | + const padData = numNode % 2 === 1 ? Buffer.concat([data, zeroHash(0)]) : data; |
| 122 | + hasher.merkleizeBlocksBytes(padData, chunkCount, output, 0); |
| 123 | + const depth = Math.ceil(Math.log2(chunkCount)); |
| 124 | + const root = subtreeFillToContents(nodes, depth).root; |
| 125 | + expectEqualHex(output, root); |
| 126 | + }); |
| 127 | + } |
| 128 | + } |
| 129 | + }); |
130 | 130 |
|
131 |
| - it(`${hasher.name} should throw error if not multiple of 64 bytes`, () => { |
132 |
| - const data = Buffer.alloc(63, 0); |
133 |
| - const output = Buffer.alloc(32); |
134 |
| - expect(() => hasher.merkleizeBlockArray([data], 1, 2, output, 0)).to.throw( |
135 |
| - "Invalid block length, expect to be 64 bytes, got 63" |
136 |
| - ); |
137 |
| - }); |
| 131 | + /** |
| 132 | + * The same to the previous test, but using the merkleizeBlockArray method |
| 133 | + */ |
| 134 | + describe("hasher.merkleizeBlockArray", function () { |
| 135 | + for (const hasher of hashers) { |
| 136 | + it(`${hasher.name} should throw error if invalid blockLimit`, () => { |
| 137 | + const data = Buffer.alloc(64, 0); |
| 138 | + const output = Buffer.alloc(32); |
| 139 | + expect(() => hasher.merkleizeBlockArray([data], 2, 2, output, 0)).to.throw( |
| 140 | + "Invalid blockLimit, expect to be less than or equal blocks.length 1, got 2" |
| 141 | + ); |
| 142 | + }); |
138 | 143 |
|
139 |
| - it(`${hasher.name} should throw error if chunkCount < 1`, () => { |
140 |
| - const data = Buffer.alloc(64, 0); |
141 |
| - const output = Buffer.alloc(32); |
142 |
| - const chunkCount = 0; |
143 |
| - expect(() => hasher.merkleizeBlockArray([data], 1, chunkCount, output, 0)).to.throw( |
144 |
| - "Invalid padFor, expect to be at least 1, got 0" |
145 |
| - ); |
146 |
| - }); |
| 144 | + it(`${hasher.name} should throw error if not multiple of 64 bytes`, () => { |
| 145 | + const data = Buffer.alloc(63, 0); |
| 146 | + const output = Buffer.alloc(32); |
| 147 | + expect(() => hasher.merkleizeBlockArray([data], 1, 2, output, 0)).to.throw( |
| 148 | + "Invalid block length, expect to be 64 bytes, got 63" |
| 149 | + ); |
| 150 | + }); |
147 | 151 |
|
148 |
| - // hashtree has a buffer of 16 * 64 bytes = 32 nodes |
149 |
| - const numNodes = [64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79]; |
150 |
| - for (const numNode of numNodes) { |
151 |
| - it(`${hasher.name}.merkleizeBlockArray for ${numNode} nodes`, () => { |
152 |
| - const nodes = Array.from({length: numNode}, (_, i) => LeafNode.fromRoot(Buffer.alloc(32, i))); |
153 |
| - const data = Buffer.concat(nodes.map((node) => node.root)); |
| 152 | + it(`${hasher.name} should throw error if chunkCount < 1`, () => { |
| 153 | + const data = Buffer.alloc(64, 0); |
154 | 154 | const output = Buffer.alloc(32);
|
155 |
| - // depth of 79 nodes are 7, make it 10 to test the padding |
156 |
| - const chunkCount = Math.max(numNode, 10); |
157 |
| - const padData = numNode % 2 === 1 ? Buffer.concat([data, zeroHash(0)]) : data; |
158 |
| - expect(padData.length % 64).to.equal(0); |
159 |
| - const blocks: Uint8Array[] = []; |
160 |
| - for (let i = 0; i < padData.length; i += 64) { |
161 |
| - blocks.push(padData.slice(i, i + 64)); |
162 |
| - } |
163 |
| - const blockLimit = blocks.length; |
164 |
| - // should be able to run with above blocks, however add some redundant blocks similar to the consumer |
165 |
| - blocks.push(Buffer.alloc(64, 1)); |
166 |
| - blocks.push(Buffer.alloc(64, 2)); |
167 |
| - hasher.merkleizeBlockArray(blocks, blockLimit, chunkCount, output, 0); |
168 |
| - const depth = Math.ceil(Math.log2(chunkCount)); |
169 |
| - const root = subtreeFillToContents(nodes, depth).root; |
170 |
| - expectEqualHex(output, root); |
| 155 | + const chunkCount = 0; |
| 156 | + expect(() => hasher.merkleizeBlockArray([data], 1, chunkCount, output, 0)).to.throw( |
| 157 | + "Invalid padFor, expect to be at least 1, got 0" |
| 158 | + ); |
171 | 159 | });
|
| 160 | + |
| 161 | + // hashtree has a buffer of 16 * 64 bytes = 32 nodes |
| 162 | + const numNodes = [64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79]; |
| 163 | + for (const numNode of numNodes) { |
| 164 | + it(`${hasher.name}.merkleizeBlockArray for ${numNode} nodes`, () => { |
| 165 | + const nodes = Array.from({length: numNode}, (_, i) => LeafNode.fromRoot(Buffer.alloc(32, i))); |
| 166 | + const data = Buffer.concat(nodes.map((node) => node.root)); |
| 167 | + const output = Buffer.alloc(32); |
| 168 | + // depth of 79 nodes are 7, make it 10 to test the padding |
| 169 | + const chunkCount = Math.max(numNode, 10); |
| 170 | + const padData = numNode % 2 === 1 ? Buffer.concat([data, zeroHash(0)]) : data; |
| 171 | + expect(padData.length % 64).to.equal(0); |
| 172 | + const blocks: Uint8Array[] = []; |
| 173 | + for (let i = 0; i < padData.length; i += 64) { |
| 174 | + blocks.push(padData.slice(i, i + 64)); |
| 175 | + } |
| 176 | + const blockLimit = blocks.length; |
| 177 | + // should be able to run with above blocks, however add some redundant blocks similar to the consumer |
| 178 | + blocks.push(Buffer.alloc(64, 1)); |
| 179 | + blocks.push(Buffer.alloc(64, 2)); |
| 180 | + hasher.merkleizeBlockArray(blocks, blockLimit, chunkCount, output, 0); |
| 181 | + const depth = Math.ceil(Math.log2(chunkCount)); |
| 182 | + const root = subtreeFillToContents(nodes, depth).root; |
| 183 | + expectEqualHex(output, root); |
| 184 | + }); |
| 185 | + } |
172 | 186 | }
|
173 |
| - } |
| 187 | + }); |
174 | 188 | });
|
0 commit comments