From 2dfb96a4e37e359c1c20f694ef6f627fa457ed7c Mon Sep 17 00:00:00 2001 From: Calvin Kim Date: Mon, 9 May 2022 18:14:45 +0900 Subject: [PATCH 1/3] accumulator_test: Update TestUndo Add two additional test cases, better error messages, and call newly added test functions to TestUndo. --- accumulator_test.go | 81 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 17 deletions(-) diff --git a/accumulator_test.go b/accumulator_test.go index 52cf30e7..b426f55e 100644 --- a/accumulator_test.go +++ b/accumulator_test.go @@ -45,6 +45,11 @@ func TestUndo(t *testing.T) { dels []Hash adds []Leaf }{ + { + 6, + []Hash{{6}, {4}, {2}, {1}, {3}}, + []Leaf{{Hash: Hash{7}}, {Hash: Hash{8}}}, + }, { 8, []Hash{{5}, {6}}, @@ -65,6 +70,11 @@ func TestUndo(t *testing.T) { []Hash{{4}, {5}}, []Leaf{{Hash: Hash{9}}, {Hash: Hash{10}}}, }, + { + 8, + []Hash{{2}, {3}, {7}}, + []Leaf{{Hash: Hash{9}}, {Hash: Hash{10}}}, + }, { 7, []Hash{{5}, {6}}, @@ -81,12 +91,12 @@ func TestUndo(t *testing.T) { for _, test := range tests { p := NewAccumulator(true) - // Create the starting off pollard. adds := make([]Leaf, test.startLeafCount) for i := range adds { adds[i].Hash[0] = uint8(i + 1) } + // Create the initial starting off pollard. err := p.Modify(adds, nil, nil) if err != nil { t.Fatal(err) @@ -99,30 +109,60 @@ func TestUndo(t *testing.T) { t.Fatal(err) } + err = proofSanity(bp) + if err != nil { + t.Fatal(err) + } + + beforeStr := p.String() + + // Perform the modify to undo. err = p.Modify(test.adds, test.dels, bp.Targets) if err != nil { t.Fatal(err) } - for _, root := range p.roots { - if root.lNiece != nil && root.rNiece != nil { - err = checkHashes(root.lNiece, root.rNiece, &p) - if err != nil { - t.Fatal(err) - } - } + afterStr := p.String() + + err = p.posMapSanity() + if err != nil { + str := fmt.Errorf("TestUndo fail: error %v"+ + "\nbefore:\n\n%s"+ + "\nafter:\n\n%s", + err, + beforeStr, + afterStr) + t.Fatal(str) + } + + err = p.checkHashes() + if err != nil { + str := fmt.Errorf("TestUndo fail: error %v"+ + "\nbefore:\n\n%s"+ + "\nafter:\n\n%s", + err, + beforeStr, + afterStr) + t.Fatal(str) } + // Perform the undo. err = p.Undo(uint64(len(test.adds)), bp.Targets, test.dels) if err != nil { t.Fatal(err) } - for _, root := range p.roots { - if root.lNiece != nil && root.rNiece != nil { - err = checkHashes(root.lNiece, root.rNiece, &p) - if err != nil { - t.Fatal(err) - } - } + undoStr := p.String() + + err = p.checkHashes() + if err != nil { + err := fmt.Errorf("TestUndo fail: error %v"+ + "\nbefore:\n\n%s"+ + "\nafter:\n\n%s"+ + "\nundo:\n\n%s", + err, + beforeStr, + afterStr, + undoStr) + t.Fatal(err) } if uint64(len(p.nodeMap)) != p.numLeaves-p.numDels { err := fmt.Errorf("TestUndo fail have %d leaves in map but only %d leaves in total", @@ -131,7 +171,14 @@ func TestUndo(t *testing.T) { } err = p.posMapSanity() if err != nil { - err := fmt.Errorf("TestUndo fail: error %v", err) + err := fmt.Errorf("TestUndo fail: error %v"+ + "\nbefore:\n\n%s"+ + "\nafter:\n\n%s"+ + "\nundo:\n\n%s", + err, + beforeStr, + afterStr, + undoStr) t.Fatal(err) } @@ -141,7 +188,7 @@ func TestUndo(t *testing.T) { beforeStr := printHashes(beforeRoots) afterStr := printHashes(afterRoots) - err := fmt.Errorf("PollardUndo Fail: roots don't equal, before %v, after %v", + err := fmt.Errorf("TestUndo Fail: roots don't equal, before %v, after %v", beforeStr, afterStr) t.Fatal(err) } From 0f94c93dfb50e5dd090dcac638c3ce83081c915d Mon Sep 17 00:00:00 2001 From: Calvin Kim Date: Mon, 9 May 2022 18:21:18 +0900 Subject: [PATCH 2/3] accumulator_test: Add fuzz tests for Modify Previously removed TestPollardAddDel is now replaced with FuzzPollardModifyChain. This provides an automated way to test for failures that happened with simChain when using different durations and random seeds. FuzzModify is also added. This provides a fuzzing without the chain and only goes through 2 total modifications. The pollard trees are also smaller, making it easier to visualize when fixing a failure. --- accumulator_test.go | 164 ++++++++++++++++++ ...d763fa478c11f18b3f8dd544f8224be81f5f151f2d | 4 + ...b29751d0ae79f5839966b64c029e9367451992bbeb | 4 + 3 files changed, 172 insertions(+) create mode 100644 testdata/fuzz/FuzzModify/e23b7af47c6eae384543a6d763fa478c11f18b3f8dd544f8224be81f5f151f2d create mode 100644 testdata/fuzz/FuzzModifyChain/4962dca5121bb6707c7320b29751d0ae79f5839966b64c029e9367451992bbeb diff --git a/accumulator_test.go b/accumulator_test.go index b426f55e..0a3f24c3 100644 --- a/accumulator_test.go +++ b/accumulator_test.go @@ -579,3 +579,167 @@ func proofSanity(proof Proof) error { return nil } + +func FuzzModify(f *testing.F) { + var tests = []struct { + startLeaves uint32 + modifyAdds uint32 + delCount uint32 + }{ + { + 8, + 2, + 3, + }, + { + 6, + 2, + 5, + }, + } + for _, test := range tests { + f.Add(test.startLeaves, test.modifyAdds, test.delCount) + } + + f.Fuzz(func(t *testing.T, startLeaves uint32, modifyAdds uint32, delCount uint32) { + // delCount must be less than the current number of leaves. + if delCount > startLeaves { + return + } + + p := NewAccumulator(true) + leaves, delHashes, delTargets := getAddsAndDels(uint32(p.numLeaves), startLeaves, delCount) + err := p.Modify(leaves, nil, nil) + if err != nil { + t.Fatal(err) + } + beforeStr := p.String() + beforeMap := nodeMapToString(p.nodeMap) + + modifyLeaves, _, _ := getAddsAndDels(uint32(p.numLeaves), modifyAdds, 0) + err = p.Modify(modifyLeaves, delHashes, delTargets) + if err != nil { + t.Fatal(err) + } + afterStr := p.String() + afterMap := nodeMapToString(p.nodeMap) + + err = p.checkHashes() + if err != nil { + t.Fatal(err) + } + + if uint64(len(p.nodeMap)) != p.numLeaves-p.numDels { + startHashes := make([]Hash, len(leaves)) + for i, leaf := range leaves { + startHashes[i] = leaf.Hash + } + + modifyHashes := make([]Hash, len(modifyLeaves)) + for i, leaf := range modifyLeaves { + modifyHashes[i] = leaf.Hash + } + err := fmt.Errorf("FuzzModify fail: have %d leaves in map but %d leaves in total. "+ + "\nbefore:\n\n%s"+ + "\nafter:\n\n%s"+ + "\nstartLeaves %d, modifyAdds %d, delCount %d, "+ + "\nstartHashes:\n%s"+ + "\nmodifyAdds:\n%s"+ + "\nmodifyDels:\n%s"+ + "\ndel targets:\n %v"+ + "\nnodemap before modify:\n %s"+ + "\nnodemap after modify:\n %s", + len(p.nodeMap), p.numLeaves-p.numDels, + beforeStr, + afterStr, + startLeaves, modifyAdds, delCount, + printHashes(startHashes), + printHashes(modifyHashes), + printHashes(delHashes), + delTargets, + beforeMap, + afterMap) + t.Fatal(err) + } + + err = p.posMapSanity() + if err != nil { + t.Fatal(err) + } + }) +} + +func FuzzModifyChain(f *testing.F) { + var tests = []struct { + numAdds uint32 + duration uint32 + seed int64 + }{ + {3, 0x07, 0x07}, + } + for _, test := range tests { + f.Add(test.numAdds, test.duration, test.seed) + } + + f.Fuzz(func(t *testing.T, numAdds, duration uint32, seed int64) { + // simulate blocks with simchain + sc := newSimChainWithSeed(duration, seed) + + p := NewAccumulator(true) + var totalAdds, totalDels int + for b := 0; b <= 100; b++ { + adds, _, delHashes := sc.NextBlock(numAdds) + totalAdds += len(adds) + totalDels += len(delHashes) + + proof, err := p.Prove(delHashes) + if err != nil { + t.Fatalf("FuzzModifyChain fail at block %d. Error: %v", b, err) + } + + err = p.Verify(delHashes, proof) + if err != nil { + t.Fatal(err) + } + + for _, target := range proof.Targets { + n, _, _, err := p.getNode(target) + if err != nil { + t.Fatalf("FuzzModifyChain fail at block %d. Error: %v", b, err) + } + if n == nil { + t.Fatalf("FuzzModifyChain fail to read %d at block %d.", target, b) + } + } + + err = p.Modify(adds, delHashes, proof.Targets) + if err != nil { + t.Fatalf("FuzzModifyChain fail at block %d. Error: %v", b, err) + } + + if b%10 == 0 { + err = p.checkHashes() + if err != nil { + t.Fatal(err) + } + } + + err = p.posMapSanity() + if err != nil { + t.Fatalf("FuzzModifyChain fail at block %d. Error: %v", + b, err) + } + if uint64(len(p.nodeMap)) != p.numLeaves-p.numDels { + err := fmt.Errorf("FuzzModifyChain fail at block %d: "+ + "have %d leaves in map but only %d leaves in total", + b, len(p.nodeMap), p.numLeaves-p.numDels) + t.Fatal(err) + } + + err = p.positionSanity() + if err != nil { + t.Fatal(err) + } + } + }) +} diff --git a/testdata/fuzz/FuzzModify/e23b7af47c6eae384543a6d763fa478c11f18b3f8dd544f8224be81f5f151f2d b/testdata/fuzz/FuzzModify/e23b7af47c6eae384543a6d763fa478c11f18b3f8dd544f8224be81f5f151f2d new file mode 100644 index 00000000..2de4b177 --- /dev/null +++ b/testdata/fuzz/FuzzModify/e23b7af47c6eae384543a6d763fa478c11f18b3f8dd544f8224be81f5f151f2d @@ -0,0 +1,4 @@ +go test fuzz v1 +uint32(1125) +uint32(119) +uint32(1020) diff --git a/testdata/fuzz/FuzzModifyChain/4962dca5121bb6707c7320b29751d0ae79f5839966b64c029e9367451992bbeb b/testdata/fuzz/FuzzModifyChain/4962dca5121bb6707c7320b29751d0ae79f5839966b64c029e9367451992bbeb new file mode 100644 index 00000000..89316c7b --- /dev/null +++ b/testdata/fuzz/FuzzModifyChain/4962dca5121bb6707c7320b29751d0ae79f5839966b64c029e9367451992bbeb @@ -0,0 +1,4 @@ +go test fuzz v1 +uint32(98) +uint32(7) +int64(7) From 12bcd56b82be7a8cf5224cf67c585ef22a5e8b54 Mon Sep 17 00:00:00 2001 From: Calvin Kim Date: Mon, 9 May 2022 18:38:48 +0900 Subject: [PATCH 3/3] accumulator_test: Add fuzzing for Undo Fuzzing tests FuzzUndo and FuzzUndoChain are added. Like FuzzModify and FuzzModifyChain, FuzzUndo only does 2 modifications and un-do's the last modification while FuzzUndoChain un-do's modifies in an interval. There's still a bug with the Undo function so the tests fail. --- accumulator_test.go | 450 +++++++++++++----- ...8e56d61f1cc6f7ac6f6b82a4f83b9c4920c269ef75 | 4 + ...a6bb7003be4c50f9353d800eec72eda2da49ccdac7 | 4 + ...8bd95b79e427aae70c55515d4119d3cf83540a26a8 | 4 + ...cc841eb23a20944c87b5a1cc5ac40bee347fffba99 | 4 + ...52f4875658a0403c00678f0048448b7e29b070cf05 | 4 + ...bd157b08c10ec0300da6370e876b096d6e36a9865e | 4 + ...6a0827204349ea1b29ef86cb9992158e7214f57c75 | 4 + ...ef37bde0e27ccbf5430bfc355ee743051a9e84890a | 4 + ...5a7a5f2e82e041aeec297bbe942db27e52aef96cc9 | 4 + ...54a2be10b7aff35bcf11d62ccf78e1dde986aac69a | 4 + 11 files changed, 379 insertions(+), 111 deletions(-) create mode 100644 testdata/fuzz/FuzzUndo/0a6defdff018759a7667608e56d61f1cc6f7ac6f6b82a4f83b9c4920c269ef75 create mode 100644 testdata/fuzz/FuzzUndo/34861bb029587905736702a6bb7003be4c50f9353d800eec72eda2da49ccdac7 create mode 100644 testdata/fuzz/FuzzUndo/59159c2f913c03a6fa2d018bd95b79e427aae70c55515d4119d3cf83540a26a8 create mode 100644 testdata/fuzz/FuzzUndo/75a507bbf3f716286f26decc841eb23a20944c87b5a1cc5ac40bee347fffba99 create mode 100644 testdata/fuzz/FuzzUndo/8f59b01f01d207e7487bb052f4875658a0403c00678f0048448b7e29b070cf05 create mode 100644 testdata/fuzz/FuzzUndo/b28763ffbfb0e518fd0c56bd157b08c10ec0300da6370e876b096d6e36a9865e create mode 100644 testdata/fuzz/FuzzUndo/bdd395e69a318ee44de06a6a0827204349ea1b29ef86cb9992158e7214f57c75 create mode 100644 testdata/fuzz/FuzzUndo/e85c8b746b7163b9ebf8e5ef37bde0e27ccbf5430bfc355ee743051a9e84890a create mode 100644 testdata/fuzz/FuzzUndo/edd25ec0fbe4995d707ef45a7a5f2e82e041aeec297bbe942db27e52aef96cc9 create mode 100644 testdata/fuzz/FuzzUndoChain/14bb5c6c12f2f48edf047454a2be10b7aff35bcf11d62ccf78e1dde986aac69a diff --git a/accumulator_test.go b/accumulator_test.go index 0a3f24c3..cabcfcef 100644 --- a/accumulator_test.go +++ b/accumulator_test.go @@ -195,117 +195,6 @@ func TestUndo(t *testing.T) { } } -func TestRandUndo(t *testing.T) { - t.Parallel() - - p := NewAccumulator(true) - - sc := newSimChain(0x07) - numAdds := uint32(5) - for b := 0; b <= 1000; b++ { - adds, durations, delHashes := sc.NextBlock(numAdds) - - bp, err := p.Prove(delHashes) - if err != nil { - t.Fatalf("TestPollardUndoRand fail at block %d. Error: %v", b, err) - } - undoTargs := make([]uint64, len(bp.Targets)) - copy(undoTargs, bp.Targets) - - undoDelHashes := make([]Hash, len(delHashes)) - copy(undoDelHashes, delHashes) - - err = p.Verify(delHashes, bp) - if err != nil { - t.Fatal(err) - } - - // We'll be comparing 3 things. Roots, nodeMap and leaf count. - beforeRoot := p.GetRoots() - beforeMap := make(map[miniHash]polNode) - for key, value := range p.nodeMap { - beforeMap[key] = *value - } - beforeLeaves := p.numLeaves - - err = p.Modify(adds, delHashes, bp.Targets) - if err != nil { - t.Fatalf("TestRandUndo fail at block %d. Error: %v", b, err) - } - - if p.numLeaves-uint64(len(adds)) != beforeLeaves { - err := fmt.Errorf("TestRandUndo fail at block %d. "+ - "Added %d leaves but have %d leaves after modify", - b, len(adds), p.numLeaves) - t.Fatal(err) - } - - // Undo the last modify. - if b%3 == 0 { - err := p.Undo(uint64(len(adds)), undoTargs, undoDelHashes) - if err != nil { - t.Fatal(err) - } - - sc.BackOne(adds, durations, delHashes) - afterRoot := p.GetRoots() - if !reflect.DeepEqual(beforeRoot, afterRoot) { - err := fmt.Errorf("TestRandUndo fail at block %d, "+ - "root mismatch. Before %s, after %s", - b, printHashes(beforeRoot), printHashes(afterRoot)) - t.Fatal(err) - } - - if len(p.nodeMap) != len(beforeMap) { - err := fmt.Errorf("TestRandUndo fail at block %d, map length mismatch. "+ - "before %d, after %d", b, len(beforeMap), len(p.nodeMap)) - t.Fatal(err) - } - - for key, value := range beforeMap { - node, found := p.nodeMap[key] - if !found { - err := fmt.Errorf("TestRandUndo fail at block %d, hash %s not found after undo", - b, hex.EncodeToString(key[:])) - t.Fatal(err) - } - - if node.data != value.data { - err := fmt.Errorf("TestRandUndo fail at block %d, for hash %s, expected %s, got %s ", - b, hex.EncodeToString(key[:]), - hex.EncodeToString(value.data[:]), - hex.EncodeToString(node.data[:])) - t.Fatal(err) - } - } - } - - // Check hashes. - if b%500 == 0 { - for _, root := range p.roots { - if root.lNiece != nil && root.rNiece != nil { - err = checkHashes(root.lNiece, root.rNiece, &p) - if err != nil { - t.Fatal(err) - } - } - } - } - if uint64(len(p.nodeMap)) != p.numLeaves-p.numDels { - err := fmt.Errorf("TestUndoRand fail at block %d: "+ - "have %d leaves in map but only %d leaves in total", - b, len(p.nodeMap), p.numLeaves-p.numDels) - t.Fatal(err) - } - - err = p.posMapSanity() - if err != nil { - err := fmt.Errorf("TestUndoRand fail at block %d: error %v", b, err) - t.Fatal(err) - } - } -} - // checkHashes moves down the tree and calculates the parent hash from the children. // It errors if the calculated hash doesn't match the hash found in the pollard. func checkHashes(node, sibling *polNode, p *Pollard) error { @@ -743,3 +632,342 @@ func FuzzModifyChain(f *testing.F) { } }) } + +func FuzzUndo(f *testing.F) { + var tests = []struct { + startLeaves uint8 + modifyAdds uint8 + delCount uint8 + }{ + { + 8, + 2, + 3, + }, + { + 6, + 2, + 5, + }, + } + + for _, test := range tests { + f.Add(test.startLeaves, test.modifyAdds, test.delCount) + } + + f.Fuzz(func(t *testing.T, startLeaves uint8, modifyAdds uint8, delCount uint8) { + if startLeaves > 32 || modifyAdds > 32 || startLeaves == 0 { + return + } + // delCount must be less than the current number of leaves. + if delCount > startLeaves { + return + } + + // Create the starting off pollard. + p := NewAccumulator(true) + leaves, dels, _ := getAddsAndDels(uint32(p.numLeaves), uint32(startLeaves), uint32(delCount)) + err := p.Modify(leaves, nil, nil) + if err != nil { + t.Fatal(err) + } + + beforeStr := p.String() + + beforeRoots := p.GetRoots() + beforeMap := nodeMapToString(p.nodeMap) + + bp, err := p.Prove(dels) + if err != nil { + t.Fatal(err) + } + + err = proofSanity(bp) + if err != nil { + t.Fatal(err) + } + + if p.numLeaves != 1 && len(bp.Targets) != len(dels) { + err := fmt.Errorf("Have %d targets but %d target hashes", + len(bp.Targets), len(dels)) + t.Fatal(err) + } + + modifyLeaves, _, _ := getAddsAndDels(uint32(p.numLeaves), uint32(modifyAdds), 0) + err = p.Modify(modifyLeaves, dels, bp.Targets) + if err != nil { + t.Fatal(err) + } + afterStr := p.String() + afterMap := nodeMapToString(p.nodeMap) + + err = p.Undo(uint64(modifyAdds), bp.Targets, dels) + if err != nil { + startHashes := make([]Hash, len(leaves)) + for i, leaf := range leaves { + startHashes[i] = leaf.Hash + } + + modifyHashes := make([]Hash, len(modifyLeaves)) + for i, leaf := range modifyLeaves { + modifyHashes[i] = leaf.Hash + } + err := fmt.Errorf("FuzzUndo fail: Undo failed, error: %v"+ + "\nbefore:\n\n%s"+ + "\nafter:\n\n%s"+ + "\nstartLeaves %d, modifyAdds %d, delCount %d"+ + "\nstartHashes:\n%s"+ + "\nmodifyAdds:\n%s"+ + "\nmodifyDels:\n%s"+ + "\ndel targets:\n %v"+ + "\nnodemap before modify:\n %s"+ + "\nnodemap after modify:\n %s", + err, + beforeStr, + afterStr, + startLeaves, modifyAdds, delCount, + printHashes(startHashes), + printHashes(modifyHashes), + printHashes(dels), + bp.Targets, + beforeMap, + afterMap) + t.Fatal(err) + } + undoStr := p.String() + undoMap := nodeMapToString(p.nodeMap) + + // Check that all the parent hashes are correct after the undo. + err = p.checkHashes() + if err != nil { + t.Fatal(err) + } + + if uint64(len(p.nodeMap)) != p.numLeaves-p.numDels { + startHashes := make([]Hash, len(leaves)) + for i, leaf := range leaves { + startHashes[i] = leaf.Hash + } + + modifyHashes := make([]Hash, len(modifyLeaves)) + for i, leaf := range modifyLeaves { + modifyHashes[i] = leaf.Hash + } + err := fmt.Errorf("FuzzUndo fail: have %d leaves in map but %d leaves in total. "+ + "\nbefore:\n\n%s"+ + "\nafter:\n\n%s"+ + "\nundo:\n\n%s"+ + "\nstartLeaves %d, modifyAdds %d, delCount %d, "+ + "\nstartHashes:\n%s"+ + "\nmodifyAdds:\n%s"+ + "\nmodifyDels:\n%s"+ + "\ndel targets:\n %v"+ + "\nnodemap before modify:\n %s"+ + "\nnodemap after modify:\n %s"+ + "\nnodemap after undo:\n %s", + len(p.nodeMap), p.numLeaves-p.numDels, + beforeStr, + afterStr, + undoStr, + startLeaves, modifyAdds, delCount, + printHashes(startHashes), + printHashes(modifyHashes), + printHashes(dels), + bp.Targets, + beforeMap, + afterMap, + undoMap) + t.Fatal(err) + } + err = p.posMapSanity() + if err != nil { + startHashes := make([]Hash, len(leaves)) + for i, leaf := range leaves { + startHashes[i] = leaf.Hash + } + + modifyHashes := make([]Hash, len(modifyLeaves)) + for i, leaf := range modifyLeaves { + modifyHashes[i] = leaf.Hash + } + t.Fatal(fmt.Errorf("FuzzUndo fail: error %v"+ + "\nbefore\n %s"+ + "\nafter\n %s"+ + "\nundo\n %s"+ + "\nstartLeaves %d, modifyAdds %d, delCount %d, "+ + "\nadds:\n%s"+ + "\nmodifyAdds:\n%s"+ + "\ndels:\n%s"+ + "\ndel targets: %v"+ + "\nnodemap:\n%s", + err, + beforeStr, + afterStr, + undoStr, + startLeaves, modifyAdds, delCount, + printHashes(startHashes), + printHashes(modifyHashes), + printHashes(dels), + bp.Targets, + nodeMapToString(p.nodeMap))) + } + + afterRoots := p.GetRoots() + + if !reflect.DeepEqual(beforeRoots, afterRoots) { + startHashes := make([]Hash, len(leaves)) + for i, leaf := range leaves { + startHashes[i] = leaf.Hash + } + + modifyHashes := make([]Hash, len(modifyLeaves)) + for i, leaf := range modifyLeaves { + modifyHashes[i] = leaf.Hash + } + + err := fmt.Errorf("FuzzUndo fail: roots don't equal after the undo. "+ + "\nbefore:\n\n%s"+ + "\nafter:\n\n%s"+ + "\nundo:\n\n%s"+ + "\nstartLeaves %d, modifyAdds %d, delCount %d, "+ + "\nstartHashes:\n%s"+ + "\nmodifyAdds:\n%s"+ + "\nmodifyDels:\n%s"+ + "\ndel targets:\n %v"+ + "\nnodemap before modify:\n %s"+ + "\nnodemap after modify:\n %s"+ + "\nnodemap after undo:\n %s", + beforeStr, + afterStr, + undoStr, + startLeaves, modifyAdds, delCount, + printHashes(startHashes), + printHashes(modifyHashes), + printHashes(dels), + bp.Targets, + beforeMap, + afterMap, + undoMap) + t.Fatal(err) + } + }) +} + +func FuzzUndoChain(f *testing.F) { + var tests = []struct { + numAdds uint32 + duration uint32 + seed int64 + }{ + {3, 0x07, 0x07}, + } + for _, test := range tests { + f.Add(test.numAdds, test.duration, test.seed) + } + + f.Fuzz(func(t *testing.T, numAdds, duration uint32, seed int64) { + p := NewAccumulator(true) + + // simulate blocks with simchain + sc := newSimChainWithSeed(duration, seed) + for b := 0; b <= 100; b++ { + adds, durations, delHashes := sc.NextBlock(numAdds) + + bp, err := p.Prove(delHashes) + if err != nil { + t.Fatalf("FuzzUndoChain fail at block %d. Error: %v", b, err) + } + undoTargs := make([]uint64, len(bp.Targets)) + copy(undoTargs, bp.Targets) + + undoDelHashes := make([]Hash, len(delHashes)) + copy(undoDelHashes, delHashes) + + err = p.Verify(delHashes, bp) + if err != nil { + t.Fatal(err) + } + + // We'll be comparing 3 things. Roots, nodeMap and leaf count. + beforeRoot := p.GetRoots() + beforeMap := make(map[miniHash]polNode) + for key, value := range p.nodeMap { + beforeMap[key] = *value + } + beforeLeaves := p.numLeaves + + err = p.Modify(adds, delHashes, bp.Targets) + if err != nil { + t.Fatalf("FuzzUndoChain fail at block %d. Error: %v", b, err) + } + + if p.numLeaves-uint64(len(adds)) != beforeLeaves { + err := fmt.Errorf("FuzzUndoChain fail at block %d. "+ + "Added %d leaves but have %d leaves after modify", + b, len(adds), p.numLeaves) + t.Fatal(err) + } + + // Undo the last modify. + if b%3 == 2 { + err := p.Undo(uint64(len(adds)), undoTargs, undoDelHashes) + if err != nil { + t.Fatal(err) + } + + sc.BackOne(adds, durations, delHashes) + afterRoot := p.GetRoots() + if !reflect.DeepEqual(beforeRoot, afterRoot) { + err := fmt.Errorf("FuzzUndoChain fail at block %d, "+ + "root mismatch.\nBefore:\n%s\nAfter:\n%s", + b, printHashes(beforeRoot), printHashes(afterRoot)) + t.Fatal(err) + } + + if len(p.nodeMap) != len(beforeMap) { + err := fmt.Errorf("FuzzUndoChain fail at block %d, map length mismatch. "+ + "before %d, after %d", b, len(beforeMap), len(p.nodeMap)) + t.Fatal(err) + } + + for key, value := range beforeMap { + node, found := p.nodeMap[key] + if !found { + err := fmt.Errorf("FuzzUndoChain fail at block %d, hash %s not found after undo", + b, hex.EncodeToString(key[:])) + t.Fatal(err) + } + + if node.data != value.data { + err := fmt.Errorf("FuzzUndoChain fail at block %d, for hash %s, expected %s, got %s ", + b, hex.EncodeToString(key[:]), + hex.EncodeToString(value.data[:]), + hex.EncodeToString(node.data[:])) + t.Fatal(err) + } + } + } + + // Check hashes. + if b%10 == 0 { + err = p.checkHashes() + if err != nil { + t.Fatal(err) + } + } + + if uint64(len(p.nodeMap)) != p.numLeaves-p.numDels { + err := fmt.Errorf("FuzzUndoChain fail at block %d: "+ + "have %d leaves in map but only %d leaves in total", + b, len(p.nodeMap), p.numLeaves-p.numDels) + t.Fatal(err) + } + + err = p.posMapSanity() + if err != nil { + err := fmt.Errorf("FuzzUndoChain fail at block %d: error %v", b, err) + t.Fatal(err) + } + } + }) +} diff --git a/testdata/fuzz/FuzzUndo/0a6defdff018759a7667608e56d61f1cc6f7ac6f6b82a4f83b9c4920c269ef75 b/testdata/fuzz/FuzzUndo/0a6defdff018759a7667608e56d61f1cc6f7ac6f6b82a4f83b9c4920c269ef75 new file mode 100644 index 00000000..9a487818 --- /dev/null +++ b/testdata/fuzz/FuzzUndo/0a6defdff018759a7667608e56d61f1cc6f7ac6f6b82a4f83b9c4920c269ef75 @@ -0,0 +1,4 @@ +go test fuzz v1 +byte('\t') +byte('j') +byte('\b') diff --git a/testdata/fuzz/FuzzUndo/34861bb029587905736702a6bb7003be4c50f9353d800eec72eda2da49ccdac7 b/testdata/fuzz/FuzzUndo/34861bb029587905736702a6bb7003be4c50f9353d800eec72eda2da49ccdac7 new file mode 100644 index 00000000..6ac3a6b4 --- /dev/null +++ b/testdata/fuzz/FuzzUndo/34861bb029587905736702a6bb7003be4c50f9353d800eec72eda2da49ccdac7 @@ -0,0 +1,4 @@ +go test fuzz v1 +byte('\n') +byte('\x02') +byte('\x05') diff --git a/testdata/fuzz/FuzzUndo/59159c2f913c03a6fa2d018bd95b79e427aae70c55515d4119d3cf83540a26a8 b/testdata/fuzz/FuzzUndo/59159c2f913c03a6fa2d018bd95b79e427aae70c55515d4119d3cf83540a26a8 new file mode 100644 index 00000000..ccd34dce --- /dev/null +++ b/testdata/fuzz/FuzzUndo/59159c2f913c03a6fa2d018bd95b79e427aae70c55515d4119d3cf83540a26a8 @@ -0,0 +1,4 @@ +go test fuzz v1 +byte('\x1b') +byte('\x01') +byte('\x12') diff --git a/testdata/fuzz/FuzzUndo/75a507bbf3f716286f26decc841eb23a20944c87b5a1cc5ac40bee347fffba99 b/testdata/fuzz/FuzzUndo/75a507bbf3f716286f26decc841eb23a20944c87b5a1cc5ac40bee347fffba99 new file mode 100644 index 00000000..9cb4cea7 --- /dev/null +++ b/testdata/fuzz/FuzzUndo/75a507bbf3f716286f26decc841eb23a20944c87b5a1cc5ac40bee347fffba99 @@ -0,0 +1,4 @@ +go test fuzz v1 +byte('\x03') +byte('\x13') +byte('\x03') diff --git a/testdata/fuzz/FuzzUndo/8f59b01f01d207e7487bb052f4875658a0403c00678f0048448b7e29b070cf05 b/testdata/fuzz/FuzzUndo/8f59b01f01d207e7487bb052f4875658a0403c00678f0048448b7e29b070cf05 new file mode 100644 index 00000000..faff5a2e --- /dev/null +++ b/testdata/fuzz/FuzzUndo/8f59b01f01d207e7487bb052f4875658a0403c00678f0048448b7e29b070cf05 @@ -0,0 +1,4 @@ +go test fuzz v1 +byte('\x1b') +byte('\r') +byte('\x12') diff --git a/testdata/fuzz/FuzzUndo/b28763ffbfb0e518fd0c56bd157b08c10ec0300da6370e876b096d6e36a9865e b/testdata/fuzz/FuzzUndo/b28763ffbfb0e518fd0c56bd157b08c10ec0300da6370e876b096d6e36a9865e new file mode 100644 index 00000000..01d2b1ec --- /dev/null +++ b/testdata/fuzz/FuzzUndo/b28763ffbfb0e518fd0c56bd157b08c10ec0300da6370e876b096d6e36a9865e @@ -0,0 +1,4 @@ +go test fuzz v1 +byte('C') +byte('\x02') +byte('<') diff --git a/testdata/fuzz/FuzzUndo/bdd395e69a318ee44de06a6a0827204349ea1b29ef86cb9992158e7214f57c75 b/testdata/fuzz/FuzzUndo/bdd395e69a318ee44de06a6a0827204349ea1b29ef86cb9992158e7214f57c75 new file mode 100644 index 00000000..273acd88 --- /dev/null +++ b/testdata/fuzz/FuzzUndo/bdd395e69a318ee44de06a6a0827204349ea1b29ef86cb9992158e7214f57c75 @@ -0,0 +1,4 @@ +go test fuzz v1 +byte('\x01') +byte('\x01') +byte('\x01') diff --git a/testdata/fuzz/FuzzUndo/e85c8b746b7163b9ebf8e5ef37bde0e27ccbf5430bfc355ee743051a9e84890a b/testdata/fuzz/FuzzUndo/e85c8b746b7163b9ebf8e5ef37bde0e27ccbf5430bfc355ee743051a9e84890a new file mode 100644 index 00000000..00d124ea --- /dev/null +++ b/testdata/fuzz/FuzzUndo/e85c8b746b7163b9ebf8e5ef37bde0e27ccbf5430bfc355ee743051a9e84890a @@ -0,0 +1,4 @@ +go test fuzz v1 +byte('\x04') +byte('\x02') +byte('\x03') diff --git a/testdata/fuzz/FuzzUndo/edd25ec0fbe4995d707ef45a7a5f2e82e041aeec297bbe942db27e52aef96cc9 b/testdata/fuzz/FuzzUndo/edd25ec0fbe4995d707ef45a7a5f2e82e041aeec297bbe942db27e52aef96cc9 new file mode 100644 index 00000000..709d99cb --- /dev/null +++ b/testdata/fuzz/FuzzUndo/edd25ec0fbe4995d707ef45a7a5f2e82e041aeec297bbe942db27e52aef96cc9 @@ -0,0 +1,4 @@ +go test fuzz v1 +byte('\x06') +byte('\x02') +byte('\x05') diff --git a/testdata/fuzz/FuzzUndoChain/14bb5c6c12f2f48edf047454a2be10b7aff35bcf11d62ccf78e1dde986aac69a b/testdata/fuzz/FuzzUndoChain/14bb5c6c12f2f48edf047454a2be10b7aff35bcf11d62ccf78e1dde986aac69a new file mode 100644 index 00000000..921849d1 --- /dev/null +++ b/testdata/fuzz/FuzzUndoChain/14bb5c6c12f2f48edf047454a2be10b7aff35bcf11d62ccf78e1dde986aac69a @@ -0,0 +1,4 @@ +go test fuzz v1 +uint32(3) +uint32(3) +int64(-53)