Skip to content

Commit b29d688

Browse files
fix: partial dag, serialization, verification and tests
1 parent 05b0535 commit b29d688

9 files changed

+533
-297
lines changed

dag/dag.go

+231-77
Large diffs are not rendered by default.

dag/dag_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func TestFull(t *testing.T) {
1515

1616
defer os.RemoveAll(tmpDir)
1717

18-
GenerateDummyDirectory(filepath.Join(tmpDir, "input"), 6, 6)
18+
GenerateDummyDirectory(filepath.Join(tmpDir, "input"), 3, 6, 1, 3)
1919
if err != nil {
2020
t.Fatalf("Could not generate dummy directory: %s", err)
2121
}

dag/edge_test.go

+46-26
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func TestOutOfRangeLeafRequests(t *testing.T) {
5353
return // Expected for invalid ranges
5454
}
5555
// If we got a partial DAG, verify it's valid
56-
if err := partial.VerifyPartial(); err != nil {
56+
if err := partial.Verify(); err != nil {
5757
t.Errorf("Invalid partial DAG returned for range %d-%d: %v", tt.start, tt.end, err)
5858
}
5959
})
@@ -210,37 +210,40 @@ func TestInvalidPaths(t *testing.T) {
210210
}
211211

212212
func TestBrokenDags(t *testing.T) {
213-
tmpDir, err := ioutil.TempDir("", "test")
214-
if err != nil {
215-
t.Fatalf("Could not create temp directory: %s", err)
216-
}
217-
defer os.RemoveAll(tmpDir)
213+
// Create a valid DAG with known structure
214+
dagBuilder := CreateDagBuilder()
218215

219-
// Create a valid DAG first
220-
err = ioutil.WriteFile(filepath.Join(tmpDir, "test.txt"), []byte("test content"), 0644)
216+
// Create a file leaf
217+
fileBuilder := CreateDagLeafBuilder("test.txt")
218+
fileBuilder.SetType(FileLeafType)
219+
fileBuilder.SetData([]byte("test content"))
220+
fileLeaf, err := fileBuilder.BuildLeaf(nil)
221221
if err != nil {
222-
t.Fatalf("Failed to create test file: %v", err)
222+
t.Fatalf("Failed to build file leaf: %v", err)
223223
}
224+
fileLeaf.SetLabel("1")
225+
dagBuilder.AddLeaf(fileLeaf, nil)
224226

225-
dag, err := CreateDag(tmpDir, false)
227+
// Create a directory with the file
228+
dirBuilder := CreateDagLeafBuilder("testdir")
229+
dirBuilder.SetType(DirectoryLeafType)
230+
dirBuilder.AddLink("1", fileLeaf.Hash)
231+
dirLeaf, err := dirBuilder.BuildRootLeaf(dagBuilder, nil)
226232
if err != nil {
227-
t.Fatalf("Failed to create DAG: %v", err)
233+
t.Fatalf("Failed to build directory leaf: %v", err)
228234
}
235+
dagBuilder.AddLeaf(dirLeaf, nil)
236+
237+
dag := dagBuilder.BuildDag(dirLeaf.Hash)
229238

230239
t.Run("missing_leaf", func(t *testing.T) {
231240
brokenDag := &Dag{
232241
Root: dag.Root,
233242
Leafs: make(map[string]*DagLeaf),
234243
}
235-
// Copy all leaves except one
236-
var skippedOne bool
237-
for hash, leaf := range dag.Leafs {
238-
if !skippedOne {
239-
skippedOne = true
240-
continue
241-
}
242-
brokenDag.Leafs[hash] = leaf
243-
}
244+
// Only copy the root leaf
245+
brokenDag.Leafs[dag.Root] = dag.Leafs[dag.Root].Clone()
246+
244247
if err := brokenDag.Verify(); err == nil {
245248
t.Error("Expected verification to fail for DAG with missing leaf")
246249
}
@@ -251,11 +254,18 @@ func TestBrokenDags(t *testing.T) {
251254
Root: dag.Root,
252255
Leafs: make(map[string]*DagLeaf),
253256
}
254-
// Copy all leaves but corrupt one's content
257+
// Copy all leaves but corrupt file content
255258
for hash, leaf := range dag.Leafs {
256259
leafCopy := leaf.Clone()
257-
if leaf.Type == FileLeafType || leaf.Type == ChunkLeafType {
258-
leafCopy.Content = append(leafCopy.Content, []byte("corrupted")...)
260+
if leaf.Type == FileLeafType {
261+
// Create a new leaf with corrupted content
262+
builder := CreateDagLeafBuilder(leaf.ItemName)
263+
builder.SetType(leaf.Type)
264+
builder.SetData(append(leaf.Content, []byte("corrupted")...))
265+
corruptedLeaf, _ := builder.BuildLeaf(nil)
266+
// Keep original hash but use corrupted content and hash
267+
leafCopy.Content = corruptedLeaf.Content
268+
leafCopy.ContentHash = corruptedLeaf.ContentHash
259269
}
260270
brokenDag.Leafs[hash] = leafCopy
261271
}
@@ -273,7 +283,12 @@ func TestBrokenDags(t *testing.T) {
273283
for hash, leaf := range dag.Leafs {
274284
leafCopy := leaf.Clone()
275285
if len(leafCopy.ClassicMerkleRoot) > 0 {
276-
leafCopy.ClassicMerkleRoot = append(leafCopy.ClassicMerkleRoot, []byte("corrupted")...)
286+
// Create a different merkle root by changing the content
287+
builder := CreateDagLeafBuilder(leaf.ItemName)
288+
builder.SetType(leaf.Type)
289+
builder.AddLink("invalid", "invalid:hash")
290+
corruptedLeaf, _ := builder.BuildLeaf(nil)
291+
leafCopy.ClassicMerkleRoot = corruptedLeaf.ClassicMerkleRoot
277292
}
278293
brokenDag.Leafs[hash] = leafCopy
279294
}
@@ -291,8 +306,13 @@ func TestBrokenDags(t *testing.T) {
291306
for hash, leaf := range dag.Leafs {
292307
leafCopy := leaf.Clone()
293308
if len(leafCopy.Links) > 0 {
294-
// Add invalid link
295-
leafCopy.Links["invalid"] = "invalid:hash"
309+
// Add invalid link while preserving CurrentLinkCount
310+
builder := CreateDagLeafBuilder(leaf.ItemName)
311+
builder.SetType(leaf.Type)
312+
builder.AddLink("invalid", "invalid:hash")
313+
corruptedLeaf, _ := builder.BuildLeaf(nil)
314+
leafCopy.Links = corruptedLeaf.Links
315+
// CurrentLinkCount stays the same as it's part of the hash
296316
}
297317
brokenDag.Leafs[hash] = leafCopy
298318
}

dag/leaves.go

+40-19
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ func (leaf *DagLeaf) Clone() *DagLeaf {
518518
LeafCount: leaf.LeafCount,
519519
Links: make(map[string]string),
520520
AdditionalData: make(map[string]string),
521+
Proofs: make(map[string]*ClassicTreeBranch),
521522
}
522523

523524
// Deep copy maps
@@ -527,26 +528,42 @@ func (leaf *DagLeaf) Clone() *DagLeaf {
527528
for k, v := range leaf.AdditionalData {
528529
cloned.AdditionalData[k] = v
529530
}
530-
531-
// If the original leaf has a merkle tree, preserve it
532-
if leaf.MerkleTree != nil && leaf.LeafMap != nil {
533-
// Create new leaf map with same data
534-
leafMap := make(map[string]merkletree.DataBlock)
535-
for k, v := range leaf.LeafMap {
536-
if tl, ok := v.(*testLeaf); ok {
537-
leafMap[k] = &testLeaf{data: tl.data}
538-
} else {
539-
// For other types, create a new leaf with the same data
540-
data, _ := v.Serialize()
541-
leafMap[k] = &testLeaf{data: string(data)}
542-
}
531+
if leaf.Proofs != nil {
532+
for k, v := range leaf.Proofs {
533+
cloned.Proofs[k] = v
543534
}
535+
}
536+
537+
// Copy root-specific fields if this is the root leaf
538+
if leaf.Hash == leaf.ParentHash || leaf.Hash == GetHash(leaf.Hash) {
539+
cloned.LatestLabel = leaf.LatestLabel
540+
cloned.LeafCount = leaf.LeafCount
541+
cloned.ParentHash = cloned.Hash // Root is its own parent
542+
} else {
543+
cloned.ParentHash = leaf.ParentHash
544+
}
544545

545-
// Build new merkle tree with same data
546-
merkleTree, err := merkletree.New(nil, leafMap)
547-
if err == nil {
548-
cloned.MerkleTree = merkleTree
549-
cloned.LeafMap = leafMap
546+
// If leaf has multiple children according to CurrentLinkCount,
547+
// we need to handle its merkle tree state
548+
if leaf.CurrentLinkCount > 1 {
549+
if len(leaf.Links) > 1 {
550+
// Build merkle tree with current links
551+
builder := merkle_tree.CreateTree()
552+
for l, h := range leaf.Links {
553+
builder.AddLeaf(l, h)
554+
}
555+
merkleTree, leafMap, err := builder.Build()
556+
if err == nil {
557+
cloned.MerkleTree = merkleTree
558+
cloned.LeafMap = leafMap
559+
cloned.ClassicMerkleRoot = merkleTree.Root
560+
}
561+
} else {
562+
// Clear merkle tree if we don't have enough links to rebuild it
563+
cloned.MerkleTree = nil
564+
cloned.LeafMap = nil
565+
// But keep ClassicMerkleRoot as it's part of the leaf's identity
566+
cloned.ClassicMerkleRoot = leaf.ClassicMerkleRoot
550567
}
551568
}
552569

@@ -586,7 +603,11 @@ func GetLabel(hash string) string {
586603

587604
func sortMapByKeys(inputMap map[string]string) map[string]string {
588605
if inputMap == nil {
589-
return inputMap
606+
return map[string]string{}
607+
}
608+
609+
if len(inputMap) <= 0 {
610+
return map[string]string{}
590611
}
591612

592613
keys := make([]string, 0, len(inputMap))

0 commit comments

Comments
 (0)