1
1
import * as crypto from 'crypto' ;
2
2
3
- export class MerkleNode {
3
+ export interface MerkleDAGNode {
4
+ id : string ;
4
5
hash : string ;
5
- left : MerkleNode | null ;
6
- right : MerkleNode | null ;
7
-
8
- constructor ( hash : string , left : MerkleNode | null = null , right : MerkleNode | null = null ) {
9
- this . hash = hash ;
10
- this . left = left ;
11
- this . right = right ;
12
- }
13
-
14
- static serializeNode ( node : MerkleNode | null ) : any {
15
- if ( ! node ) return null ;
16
- return {
17
- hash : node . hash ,
18
- left : MerkleNode . serializeNode ( node . left ) ,
19
- right : MerkleNode . serializeNode ( node . right )
20
- } ;
21
- }
22
-
23
- static deserializeNode ( data : any ) : MerkleNode | null {
24
- if ( ! data ) return null ;
25
- return new MerkleNode (
26
- data . hash ,
27
- MerkleNode . deserializeNode ( data . left ) ,
28
- MerkleNode . deserializeNode ( data . right )
29
- ) ;
30
- }
6
+ data : string ;
7
+ parents : string [ ] ;
8
+ children : string [ ] ;
31
9
}
32
10
33
- export class MerkleTree {
34
- root : MerkleNode ;
35
- leaves : MerkleNode [ ] ;
11
+ export class MerkleDAG {
12
+ nodes : Map < string , MerkleDAGNode > ;
13
+ rootIds : string [ ] ;
36
14
37
- constructor ( data : string [ ] ) {
38
- const leaves = data . map ( d => new MerkleNode ( this . hash ( d ) ) ) ;
39
- this . leaves = leaves ;
40
- this . root = this . buildTree ( leaves ) ;
15
+ constructor ( ) {
16
+ this . nodes = new Map ( ) ;
17
+ this . rootIds = [ ] ;
41
18
}
42
19
43
20
private hash ( data : string ) : string {
44
21
return crypto . createHash ( 'sha256' ) . update ( data ) . digest ( 'hex' ) ;
45
22
}
46
23
47
- private buildTree ( nodes : MerkleNode [ ] ) : MerkleNode {
48
- if ( nodes . length === 0 ) {
49
- return new MerkleNode ( this . hash ( '' ) ) ;
50
- }
51
- if ( nodes . length === 1 ) {
52
- return nodes [ 0 ] ;
53
- }
24
+ public addNode ( data : string , parentId ?: string ) : string {
25
+ const nodeId = this . hash ( data ) ;
26
+ const node : MerkleDAGNode = {
27
+ id : nodeId ,
28
+ hash : nodeId ,
29
+ data,
30
+ parents : [ ] ,
31
+ children : [ ]
32
+ } ;
54
33
55
- const parents : MerkleNode [ ] = [ ] ;
56
- for ( let i = 0 ; i < nodes . length ; i += 2 ) {
57
- const left = nodes [ i ] ;
58
- const right = ( i + 1 < nodes . length ) ? nodes [ i + 1 ] : left ;
59
- const parentHash = this . hash ( left . hash + right . hash ) ;
60
- parents . push ( new MerkleNode ( parentHash , left , right ) ) ;
34
+ // If there's a parent, create the relationship
35
+ if ( parentId ) {
36
+ const parentNode = this . nodes . get ( parentId ) ;
37
+ if ( parentNode ) {
38
+ node . parents . push ( parentId ) ;
39
+ parentNode . children . push ( nodeId ) ;
40
+ this . nodes . set ( parentId , parentNode ) ;
41
+ }
42
+ } else {
43
+ // If no parent, it's a root node
44
+ this . rootIds . push ( nodeId ) ;
61
45
}
62
46
63
- return this . buildTree ( parents ) ;
47
+ this . nodes . set ( nodeId , node ) ;
48
+ return nodeId ;
64
49
}
65
50
66
- public getRootHash ( ) : string {
67
- return this . root . hash ;
51
+ public getNode ( nodeId : string ) : MerkleDAGNode | undefined {
52
+ return this . nodes . get ( nodeId ) ;
68
53
}
69
54
70
- public static compare ( tree1 : MerkleTree , tree2 : MerkleTree ) : { added : string [ ] , removed : string [ ] , modified : string [ ] } {
71
- const C1 = new Map ( tree1 . leaves . map ( l => [ l . hash , l ] ) ) ;
72
- const C2 = new Map ( tree2 . leaves . map ( l => [ l . hash , l ] ) ) ;
55
+ public getAllNodes ( ) : MerkleDAGNode [ ] {
56
+ return Array . from ( this . nodes . values ( ) ) ;
57
+ }
73
58
74
- const added = Array . from ( C2 . keys ( ) ) . filter ( k => ! C1 . has ( k ) ) ;
75
- const removed = Array . from ( C1 . keys ( ) ) . filter ( k => ! C2 . has ( k ) ) ;
76
-
77
- return { added, removed, modified : [ ] } ;
59
+ public getRootNodes ( ) : MerkleDAGNode [ ] {
60
+ return this . rootIds . map ( id => this . nodes . get ( id ) ! ) . filter ( Boolean ) ;
61
+ }
62
+
63
+ public getLeafNodes ( ) : MerkleDAGNode [ ] {
64
+ return Array . from ( this . nodes . values ( ) ) . filter ( node => node . children . length === 0 ) ;
78
65
}
79
66
80
67
public serialize ( ) : any {
81
68
return {
82
- root : MerkleNode . serializeNode ( this . root ) ,
83
- leaves : this . leaves . map ( l => MerkleNode . serializeNode ( l ) )
69
+ nodes : Array . from ( this . nodes . entries ( ) ) ,
70
+ rootIds : this . rootIds
84
71
} ;
85
72
}
86
73
87
- static deserialize ( data : any ) : MerkleTree {
88
- const tree = Object . create ( MerkleTree . prototype ) ;
89
- tree . root = MerkleNode . deserializeNode ( data . root ) ;
90
- tree . leaves = ( data . leaves || [ ] ) . map ( ( l : any ) => MerkleNode . deserializeNode ( l ) ) ;
91
- return tree ;
74
+ public static deserialize ( data : any ) : MerkleDAG {
75
+ const dag = new MerkleDAG ( ) ;
76
+ dag . nodes = new Map ( data . nodes ) ;
77
+ dag . rootIds = data . rootIds ;
78
+ return dag ;
79
+ }
80
+
81
+ public static compare ( dag1 : MerkleDAG , dag2 : MerkleDAG ) : { added : string [ ] , removed : string [ ] , modified : string [ ] } {
82
+ const nodes1 = new Map ( Array . from ( dag1 . getAllNodes ( ) ) . map ( n => [ n . id , n ] ) ) ;
83
+ const nodes2 = new Map ( Array . from ( dag2 . getAllNodes ( ) ) . map ( n => [ n . id , n ] ) ) ;
84
+
85
+ const added = Array . from ( nodes2 . keys ( ) ) . filter ( k => ! nodes1 . has ( k ) ) ;
86
+ const removed = Array . from ( nodes1 . keys ( ) ) . filter ( k => ! nodes2 . has ( k ) ) ;
87
+
88
+ // For modified, we'll check if the data has changed for nodes that exist in both
89
+ const modified : string [ ] = [ ] ;
90
+ for ( const [ id , node1 ] of Array . from ( nodes1 . entries ( ) ) ) {
91
+ const node2 = nodes2 . get ( id ) ;
92
+ if ( node2 && node1 . data !== node2 . data ) {
93
+ modified . push ( id ) ;
94
+ }
95
+ }
96
+
97
+ return { added, removed, modified } ;
92
98
}
93
99
}
0 commit comments