@@ -259,23 +259,108 @@ function calculateMerkleRoot(quads, chunkSizeBytes = 32) {
259259
260260function calculateMerkleProof ( quads , chunkSizeBytes , challenge ) {
261261 const chunks = splitIntoChunks ( quads , chunkSizeBytes ) ;
262+
263+ // Step 1: Generate leaf hashes using Solidity-compatible hashing
262264 const leaves = chunks . map ( ( chunk , index ) =>
263265 Buffer . from (
264266 ethers . utils
265267 . solidityKeccak256 ( [ "string" , "uint256" ] , [ chunk , index ] )
266- . replace ( "0x" , "" ) ,
268+ . slice ( 2 ) , // strip "0x"
267269 "hex"
268270 )
269271 ) ;
270272
271- const tree = new merkletreejs . MerkleTree ( leaves , arraifyKeccak256 , { sortPairs : true } ) ;
273+ const proof = [ ] ;
274+ let index = challenge ;
275+ let currentLevel = leaves ;
276+
277+ // Step 2: Traverse tree upward and build proof path
278+ while ( currentLevel . length > 1 ) {
279+ const nextLevel = [ ] ;
280+
281+ for ( let i = 0 ; i < currentLevel . length ; i += 2 ) {
282+ const left = currentLevel [ i ] ;
283+ const right = i + 1 < currentLevel . length ? currentLevel [ i + 1 ] : null ;
284+
285+ if ( right ) {
286+ // Sort pair like Solidity (<)
287+ const [ first , second ] =
288+ Buffer . compare ( left , right ) < 0 ? [ left , right ] : [ right , left ] ;
289+
290+ const combined = Buffer . concat ( [ first , second ] ) ;
291+ const parent = Buffer . from (
292+ ethers . utils . keccak256 ( combined ) . slice ( 2 ) ,
293+ "hex"
294+ ) ;
295+
296+ nextLevel . push ( parent ) ;
297+
298+ // Collect sibling if current index is part of this pair
299+ if ( i === index || i + 1 === index ) {
300+ const sibling = i === index ? right : left ;
301+ proof . push ( `0x${ sibling . toString ( "hex" ) } ` ) ;
302+ index = Math . floor ( i / 2 ) ;
303+ }
304+ } else {
305+ // Odd number of nodes – carry unpaired node up
306+ nextLevel . push ( left ) ;
307+
308+ if ( i === index ) {
309+ index = Math . floor ( i / 2 ) ;
310+ }
311+ }
312+ }
313+
314+ currentLevel = nextLevel ;
315+ }
316+
317+ const leaf = leaves [ challenge ] ;
318+ const root = currentLevel [ 0 ] ;
272319
273320 return {
274- leaf : arraifyKeccak256 ( chunks [ challenge ] ) ,
275- proof : tree . getHexProof ( leaves [ challenge ] ) ,
321+ root : `0x${ root . toString ( "hex" ) } ` ,
322+ proof,
323+ leaf : `0x${ leaf . toString ( "hex" ) } ` ,
324+ chunk : chunks [ challenge ] ,
325+ chunkId : challenge ,
276326 } ;
277327}
278328
329+ function computeMerkleRootFromProof ( chunks , chunkId , proof ) {
330+ // Get the specific chunk we're proving
331+ const challengeChunk = chunks [ chunkId ] ;
332+ if ( ! challengeChunk ) {
333+ throw new Error ( `Chunk ${ chunkId } not found in chunks array` ) ;
334+ }
335+
336+ // Calculate initial hash from the chunk and its ID
337+ let currentHash = Buffer . from (
338+ ethers . utils
339+ . solidityKeccak256 ( [ "string" , "uint256" ] , [ challengeChunk , chunkId ] )
340+ . slice ( 2 ) ,
341+ "hex"
342+ ) ;
343+
344+ // Process each proof element
345+ for ( const siblingHex of proof ) {
346+ const sibling = Buffer . from ( siblingHex . slice ( 2 ) , "hex" ) ;
347+
348+ // Ensure deterministic ordering of hashes
349+ const [ first , second ] =
350+ Buffer . compare ( currentHash , sibling ) < 0
351+ ? [ currentHash , sibling ]
352+ : [ sibling , currentHash ] ;
353+
354+ // Compute parent hash
355+ currentHash = Buffer . from (
356+ ethers . utils . keccak256 ( Buffer . concat ( [ first , second ] ) ) . slice ( 2 ) ,
357+ "hex"
358+ ) ;
359+ }
360+
361+ return `0x${ currentHash . toString ( "hex" ) } ` ;
362+ }
363+
279364function groupNquadsBySubject ( nquadsArray , sort = false ) {
280365 const parser = new N3 . Parser ( { format : "star" } ) ;
281366 const grouped = { } ;
@@ -448,6 +533,7 @@ var knowledgeCollectionTools = /*#__PURE__*/Object.freeze({
448533 calculateMerkleProof : calculateMerkleProof ,
449534 calculateMerkleRoot : calculateMerkleRoot ,
450535 calculateNumberOfChunks : calculateNumberOfChunks ,
536+ computeMerkleRootFromProof : computeMerkleRootFromProof ,
451537 countDistinctSubjects : countDistinctSubjects ,
452538 filterTriplesByAnnotation : filterTriplesByAnnotation ,
453539 formatDataset : formatDataset ,
0 commit comments