1- import { keccak256 } from 'ethereum-cryptography/keccak' ;
2- import { concatBytes , bytesToHex , equalsBytes } from 'ethereum-cryptography/utils' ;
3- import { Bytes , compareBytes } from './bytes' ;
4- import { throwError } from './utils/throw-error' ;
1+ import { keccak256 } from '@ethersproject/keccak256' ;
2+ import { BytesLike , HexString , toHex , toBytes , concat , compare } from './bytes' ;
3+ import { invariant , throwError , validateArgument } from './utils/errors' ;
54
6- const hashPair = ( a : Bytes , b : Bytes ) => keccak256 ( concatBytes ( ... [ a , b ] . sort ( compareBytes ) ) ) ;
5+ const hashPair = ( a : BytesLike , b : BytesLike ) : HexString => keccak256 ( concat ( [ a , b ] . sort ( compare ) ) ) ;
76
87const leftChildIndex = ( i : number ) => 2 * i + 1 ;
98const rightChildIndex = ( i : number ) => 2 * i + 2 ;
@@ -13,26 +12,21 @@ const siblingIndex = (i: number) => (i > 0 ? i - (-1) ** (i % 2) : throwError('R
1312const isTreeNode = ( tree : unknown [ ] , i : number ) => i >= 0 && i < tree . length ;
1413const isInternalNode = ( tree : unknown [ ] , i : number ) => isTreeNode ( tree , leftChildIndex ( i ) ) ;
1514const isLeafNode = ( tree : unknown [ ] , i : number ) => isTreeNode ( tree , i ) && ! isInternalNode ( tree , i ) ;
16- const isValidMerkleNode = ( node : Bytes ) => node instanceof Uint8Array && node . length === 32 ;
15+ const isValidMerkleNode = ( node : BytesLike ) => toBytes ( node ) . length === 32 ;
1716
18- const checkTreeNode = ( tree : unknown [ ] , i : number ) => void ( isTreeNode ( tree , i ) || throwError ( 'Index is not in tree' ) ) ;
19- const checkInternalNode = ( tree : unknown [ ] , i : number ) =>
20- void ( isInternalNode ( tree , i ) || throwError ( 'Index is not an internal tree node' ) ) ;
2117const checkLeafNode = ( tree : unknown [ ] , i : number ) => void ( isLeafNode ( tree , i ) || throwError ( 'Index is not a leaf' ) ) ;
22- const checkValidMerkleNode = ( node : Bytes ) =>
18+ const checkValidMerkleNode = ( node : BytesLike ) =>
2319 void ( isValidMerkleNode ( node ) || throwError ( 'Merkle tree nodes must be Uint8Array of length 32' ) ) ;
2420
25- export function makeMerkleTree ( leaves : Bytes [ ] ) : Bytes [ ] {
21+ export function makeMerkleTree ( leaves : BytesLike [ ] ) : HexString [ ] {
2622 leaves . forEach ( checkValidMerkleNode ) ;
2723
28- if ( leaves . length === 0 ) {
29- throw new Error ( 'Expected non-zero number of leaves' ) ;
30- }
24+ validateArgument ( leaves . length !== 0 , 'Expected non-zero number of leaves' ) ;
3125
32- const tree = new Array < Bytes > ( 2 * leaves . length - 1 ) ;
26+ const tree = new Array < HexString > ( 2 * leaves . length - 1 ) ;
3327
3428 for ( const [ i , leaf ] of leaves . entries ( ) ) {
35- tree [ tree . length - 1 - i ] = leaf ;
29+ tree [ tree . length - 1 - i ] = toHex ( leaf ) ;
3630 }
3731 for ( let i = tree . length - 1 - leaves . length ; i >= 0 ; i -- ) {
3832 tree [ i ] = hashPair ( tree [ leftChildIndex ( i ) ] ! , tree [ rightChildIndex ( i ) ] ! ) ;
@@ -41,22 +35,22 @@ export function makeMerkleTree(leaves: Bytes[]): Bytes[] {
4135 return tree ;
4236}
4337
44- export function getProof ( tree : Bytes [ ] , index : number ) : Bytes [ ] {
38+ export function getProof ( tree : BytesLike [ ] , index : number ) : HexString [ ] {
4539 checkLeafNode ( tree , index ) ;
4640
4741 const proof = [ ] ;
4842 while ( index > 0 ) {
49- proof . push ( tree [ siblingIndex ( index ) ] ! ) ;
43+ proof . push ( toHex ( tree [ siblingIndex ( index ) ] ! ) ) ;
5044 index = parentIndex ( index ) ;
5145 }
5246 return proof ;
5347}
5448
55- export function processProof ( leaf : Bytes , proof : Bytes [ ] ) : Bytes {
49+ export function processProof ( leaf : BytesLike , proof : BytesLike [ ] ) : HexString {
5650 checkValidMerkleNode ( leaf ) ;
5751 proof . forEach ( checkValidMerkleNode ) ;
5852
59- return proof . reduce ( hashPair , leaf ) ;
53+ return toHex ( proof . reduce ( hashPair , leaf ) ) ;
6054}
6155
6256export interface MultiProof < T , L = T > {
@@ -65,13 +59,14 @@ export interface MultiProof<T, L = T> {
6559 proofFlags : boolean [ ] ;
6660}
6761
68- export function getMultiProof ( tree : Bytes [ ] , indices : number [ ] ) : MultiProof < Bytes > {
62+ export function getMultiProof ( tree : BytesLike [ ] , indices : number [ ] ) : MultiProof < HexString > {
6963 indices . forEach ( i => checkLeafNode ( tree , i ) ) ;
7064 indices . sort ( ( a , b ) => b - a ) ;
7165
72- if ( indices . slice ( 1 ) . some ( ( i , p ) => i === indices [ p ] ) ) {
73- throw new Error ( 'Cannot prove duplicated index' ) ;
74- }
66+ validateArgument (
67+ indices . slice ( 1 ) . every ( ( i , p ) => i !== indices [ p ] ) ,
68+ 'Cannot prove duplicated index' ,
69+ ) ;
7570
7671 const stack = indices . concat ( ) ; // copy
7772 const proof = [ ] ;
@@ -87,54 +82,51 @@ export function getMultiProof(tree: Bytes[], indices: number[]): MultiProof<Byte
8782 stack . shift ( ) ; // consume from the stack
8883 } else {
8984 proofFlags . push ( false ) ;
90- proof . push ( tree [ s ] ! ) ;
85+ proof . push ( toHex ( tree [ s ] ! ) ) ;
9186 }
9287 stack . push ( p ) ;
9388 }
9489
9590 if ( indices . length === 0 ) {
96- proof . push ( tree [ 0 ] ! ) ;
91+ proof . push ( toHex ( tree [ 0 ] ! ) ) ;
9792 }
9893
9994 return {
100- leaves : indices . map ( i => tree [ i ] ! ) ,
95+ leaves : indices . map ( i => toHex ( tree [ i ] ! ) ) ,
10196 proof,
10297 proofFlags,
10398 } ;
10499}
105100
106- export function processMultiProof ( multiproof : MultiProof < Bytes > ) : Bytes {
101+ export function processMultiProof ( multiproof : MultiProof < BytesLike > ) : HexString {
107102 multiproof . leaves . forEach ( checkValidMerkleNode ) ;
108103 multiproof . proof . forEach ( checkValidMerkleNode ) ;
109104
110- if ( multiproof . proof . length < multiproof . proofFlags . filter ( b => ! b ) . length ) {
111- throw new Error ( 'Invalid multiproof format' ) ;
112- }
113-
114- if ( multiproof . leaves . length + multiproof . proof . length !== multiproof . proofFlags . length + 1 ) {
115- throw new Error ( 'Provided leaves and multiproof are not compatible' ) ;
116- }
105+ validateArgument (
106+ multiproof . proof . length >= multiproof . proofFlags . filter ( b => ! b ) . length ,
107+ 'Invalid multiproof format' ,
108+ ) ;
109+ validateArgument (
110+ multiproof . leaves . length + multiproof . proof . length === multiproof . proofFlags . length + 1 ,
111+ 'Provided leaves and multiproof are not compatible' ,
112+ ) ;
117113
118114 const stack = multiproof . leaves . concat ( ) ; // copy
119115 const proof = multiproof . proof . concat ( ) ; // copy
120116
121117 for ( const flag of multiproof . proofFlags ) {
122118 const a = stack . shift ( ) ;
123119 const b = flag ? stack . shift ( ) : proof . shift ( ) ;
124- if ( a === undefined || b === undefined ) {
125- throw new Error ( 'Broken invariant' ) ;
126- }
120+ invariant ( a !== undefined && b !== undefined ) ;
127121 stack . push ( hashPair ( a , b ) ) ;
128122 }
129123
130- if ( stack . length + proof . length !== 1 ) {
131- throw new Error ( 'Broken invariant' ) ;
132- }
124+ invariant ( stack . length + proof . length === 1 ) ;
133125
134- return stack . pop ( ) ?? proof . shift ( ) ! ;
126+ return toHex ( stack . pop ( ) ?? proof . shift ( ) ! ) ;
135127}
136128
137- export function isValidMerkleTree ( tree : Bytes [ ] ) : boolean {
129+ export function isValidMerkleTree ( tree : BytesLike [ ] ) : boolean {
138130 for ( const [ i , node ] of tree . entries ( ) ) {
139131 if ( ! isValidMerkleNode ( node ) ) {
140132 return false ;
@@ -147,18 +139,16 @@ export function isValidMerkleTree(tree: Bytes[]): boolean {
147139 if ( l < tree . length ) {
148140 return false ;
149141 }
150- } else if ( ! equalsBytes ( node , hashPair ( tree [ l ] ! , tree [ r ] ! ) ) ) {
142+ } else if ( compare ( node , hashPair ( tree [ l ] ! , tree [ r ] ! ) ) ) {
151143 return false ;
152144 }
153145 }
154146
155147 return tree . length > 0 ;
156148}
157149
158- export function renderMerkleTree ( tree : Bytes [ ] ) : string {
159- if ( tree . length === 0 ) {
160- throw new Error ( 'Expected non-zero number of nodes' ) ;
161- }
150+ export function renderMerkleTree ( tree : BytesLike [ ] ) : HexString {
151+ validateArgument ( tree . length !== 0 , 'Expected non-zero number of nodes' ) ;
162152
163153 const stack : [ number , number [ ] ] [ ] = [ [ 0 , [ ] ] ] ;
164154
@@ -178,7 +168,7 @@ export function renderMerkleTree(tree: Bytes[]): string {
178168 . join ( '' ) +
179169 i +
180170 ') ' +
181- bytesToHex ( tree [ i ] ! ) ,
171+ toHex ( tree [ i ] ! ) ,
182172 ) ;
183173
184174 if ( rightChildIndex ( i ) < tree . length ) {
0 commit comments