@@ -2,16 +2,19 @@ import { test, testProp, fc } from '@fast-check/ava';
2
2
import { HashZero as zero } from '@ethersproject/constants' ;
3
3
import { keccak256 } from '@ethersproject/keccak256' ;
4
4
import { SimpleMerkleTree } from './simple' ;
5
- import { BytesLike , HexString , concat , compare } from './bytes' ;
5
+ import { BytesLike , HexString , concat , compare , toHex } from './bytes' ;
6
+ import { InvalidArgumentError , InvariantError } from './utils/errors' ;
7
+
8
+ fc . configureGlobal ( { numRuns : process . env . CI ? 5000 : 100 } ) ;
6
9
7
10
const reverseNodeHash = ( a : BytesLike , b : BytesLike ) : HexString => keccak256 ( concat ( [ a , b ] . sort ( compare ) . reverse ( ) ) ) ;
8
11
const otherNodeHash = ( a : BytesLike , b : BytesLike ) : HexString => keccak256 ( reverseNodeHash ( a , b ) ) ; // double hash
9
12
10
- import { toHex } from './bytes' ;
11
- import { InvalidArgumentError , InvariantError } from './utils/errors' ;
12
-
13
- const leaf = fc . uint8Array ( { minLength : 32 , maxLength : 32 } ) . map ( toHex ) ;
14
- const leaves = fc . array ( leaf , { minLength : 1 } ) ;
13
+ // Use a mix of uint8array and hexstring to cover the Byteslike space
14
+ const leaf = fc
15
+ . uint8Array ( { minLength : 32 , maxLength : 32 } )
16
+ . chain ( l => fc . oneof ( fc . constant ( l ) , fc . constant ( toHex ( l ) ) ) ) ;
17
+ const leaves = fc . array ( leaf , { minLength : 1 , maxLength : 1000 } ) ;
15
18
const options = fc . record ( {
16
19
sortLeaves : fc . oneof ( fc . constant ( undefined ) , fc . boolean ( ) ) ,
17
20
nodeHash : fc . oneof ( fc . constant ( undefined ) , fc . constant ( reverseNodeHash ) ) ,
@@ -20,27 +23,31 @@ const options = fc.record({
20
23
const tree = fc
21
24
. tuple ( leaves , options )
22
25
. chain ( ( [ leaves , options ] ) => fc . tuple ( fc . constant ( SimpleMerkleTree . of ( leaves , options ) ) , fc . constant ( options ) ) ) ;
23
- const treeAndLeaf = fc . tuple ( leaves , options ) . chain ( ( [ leaves , options ] ) =>
26
+ const treeAndLeaf = tree . chain ( ( [ tree , options ] ) =>
24
27
fc . tuple (
25
- fc . constant ( SimpleMerkleTree . of ( leaves , options ) ) ,
28
+ fc . constant ( tree ) ,
26
29
fc . constant ( options ) ,
27
- fc . nat ( { max : leaves . length - 1 } ) . map ( index => ( { value : leaves [ index ] ! , index } ) ) ,
30
+ fc . nat ( { max : tree . length - 1 } ) . map ( index => ( { value : tree . at ( index ) ! , index } ) ) ,
28
31
) ,
29
32
) ;
30
- const treeAndLeaves = fc . tuple ( leaves , options ) . chain ( ( [ leaves , options ] ) =>
33
+ const treeAndLeaves = tree . chain ( ( [ tree , options ] ) =>
31
34
fc . tuple (
32
- fc . constant ( SimpleMerkleTree . of ( leaves , options ) ) ,
35
+ fc . constant ( tree ) ,
33
36
fc . constant ( options ) ,
34
37
fc
35
- . uniqueArray ( fc . nat ( { max : leaves . length - 1 } ) )
36
- . map ( indices => indices . map ( index => ( { value : leaves [ index ] ! , index } ) ) ) ,
38
+ . uniqueArray ( fc . nat ( { max : tree . length - 1 } ) )
39
+ . map ( indices => indices . map ( index => ( { value : tree . at ( index ) ! , index } ) ) ) ,
37
40
) ,
38
41
) ;
39
42
40
- fc . configureGlobal ( { numRuns : process . env . CI ? 10000 : 100 } ) ;
41
-
42
43
testProp ( 'generates a valid tree' , [ tree ] , ( t , [ tree ] ) => {
43
44
t . notThrows ( ( ) => tree . validate ( ) ) ;
45
+
46
+ // check leaves enumeration
47
+ for ( const [ index , value ] of tree . entries ( ) ) {
48
+ t . is ( value , tree . at ( index ) ! ) ;
49
+ }
50
+ t . is ( tree . at ( tree . length ) , undefined ) ;
44
51
} ) ;
45
52
46
53
testProp (
@@ -118,10 +125,14 @@ testProp('dump and load', [tree], (t, [tree, options]) => {
118
125
const recoveredTree = SimpleMerkleTree . load ( dump , options . nodeHash ) ;
119
126
recoveredTree . validate ( ) ; // already done in load
120
127
128
+ // check dump & reconstructed tree
129
+ t . is ( dump . format , 'simple-v1' ) ;
121
130
t . is ( dump . hash , options . nodeHash ? 'custom' : undefined ) ;
131
+ t . true ( dump . values . every ( ( { value } , index ) => value === toHex ( tree . at ( index ) ! ) ) ) ;
132
+ t . true ( dump . values . every ( ( { value } , index ) => value === toHex ( recoveredTree . at ( index ) ! ) ) ) ;
122
133
t . is ( tree . root , recoveredTree . root ) ;
134
+ t . is ( tree . length , recoveredTree . length ) ;
123
135
t . is ( tree . render ( ) , recoveredTree . render ( ) ) ;
124
- t . deepEqual ( tree . entries ( ) , recoveredTree . entries ( ) ) ;
125
136
t . deepEqual ( tree . dump ( ) , recoveredTree . dump ( ) ) ;
126
137
} ) ;
127
138
0 commit comments