Skip to content

Commit

Permalink
Fuzz forest / undo (#330)
Browse files Browse the repository at this point in the history
* less verbose undo test, check roots undone correctly

* fuzzing

* fuzz in CI
  • Loading branch information
devrandom authored Dec 20, 2021
1 parent 1deb2e5 commit cc944c1
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 17 deletions.
66 changes: 66 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,69 @@ jobs:
run: |
cd $GOPATH/src/github.com/${{ github.repository }}
./test/check_proofs_backwards.sh utreexoserver "${{ github.workspace }}"
fuzz:
name: Fuzz
runs-on: ubuntu-latest
needs: Build
timeout-minutes: 5
steps:

- name: setup directory
shell: bash
run: |
mkdir -p "${{ github.workspace }}/fuzz-coverage"
# set GOPATH
- name: setup env
shell: bash
run: |
echo "${{ github.workspace }}/go/bin:" >> $GITHUB_PATH
echo "GOPATH=${{ github.workspace }}/go" >> $GITHUB_ENV
- name: Install Go
if: success()
uses: actions/setup-go@v1
with:
# go install doesn't work with 1.13
go-version: 1.17
id: go

- name: checkout
uses: actions/checkout@v2
with:
fetch-depth: 1
path: go/src/github.com/${{ github.repository }}

- name: Get dependencies
run: |
cd $GOPATH/src/github.com/${{ github.repository }}
go get -v -d ./...
- name: Install fuzzer
shell: bash
run: |
go install github.com/dvyukov/go-fuzz/go-fuzz@latest github.com/dvyukov/go-fuzz/go-fuzz-build@latest
go get github.com/dvyukov/go-fuzz/go-fuzz-dep
- name: Build
shell: bash
run: |
cd $GOPATH/src/github.com/${{ github.repository }}/accumulator
go-fuzz-build
- name: Fuzz
shell: bash
run: |
cd $GOPATH/src/github.com/${{ github.repository }}/accumulator
timeout --preserve-status --signal INT 120s go-fuzz -dumpcover |& tee fuzz.log
grep "crashers: 0," fuzz.log > /dev/null
sed -i '/0.0,1.1/d' coverprofile
cp coverprofile "${{ github.workspace }}/fuzz-coverage/"
go tool cover -html=coverprofile -o "${{ github.workspace }}/fuzz-coverage/cover.html"
- name: Fuzz coverage
uses: actions/upload-artifact@v2
with:
name: fuzz-coverage
path: "${{ github.workspace }}/fuzz-coverage"
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ cmd/simcmd/simcmd
cmd/utreexoclient/utreexoclient
cmd/utreexoserver/utreexoserver
cmd/utreexoclient/pollardFile
/accumulator/corpus/
/accumulator/crashers/
/accumulator/suppressions/
/accumulator/accumulator-fuzz.zip
/accumulator/coverprofile
/accumulator/sonarprofile
73 changes: 73 additions & 0 deletions accumulator/fuzz.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//go:build gofuzz
// +build gofuzz

package accumulator

import (
"bytes"
"fmt"
"reflect"
)

func undoOnceFuzzy(data *bytes.Buffer) error {
f := NewForest(RamForest, nil, "", 0)

seed0, err := data.ReadByte();
if err != nil { return nil }
seed1, err := data.ReadByte();
if err != nil { return nil }
seed := (int64(seed1) << 8) | int64(seed0)
sc := newSimChainWithSeed(0x07, seed)
if sc == nil {
return nil
}
sc.lookahead = 0
for b := int32(0); ; b++ {
numAdds, err := data.ReadByte()
if err != nil {
break
}
numAdds &= 0x1f

adds, durations, delHashes := sc.NextBlock(uint32(numAdds))

bp, err := f.ProveBatch(delHashes)
if err != nil {
return err
}
beforeRoot := f.getRoots()
ub, err := f.Modify(adds, bp.Targets)
if err != nil {
return err
}
err = f.PosMapSanity()
if err != nil {
return err
}

// undo every 3rd block
if b%3 == 2 {
err := f.Undo(*ub)
if err != nil {
return err
}
sc.BackOne(adds, durations, delHashes)
afterRoot := f.getRoots()
if !reflect.DeepEqual(beforeRoot, afterRoot) {
return fmt.Errorf("undo mismatch")
}
}

}
return nil
}

func Fuzz(dataBytes []byte) int {
data := bytes.NewBuffer(dataBytes)

err := undoOnceFuzzy(data)
if err != nil {
panic("failed")
}
return 1
}
31 changes: 24 additions & 7 deletions accumulator/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ type simChain struct {
leafCounter uint64
durationMask uint32
lookahead int32
rnd *rand.Rand
}

// newSimChain initializes and returns a simchain
Expand All @@ -93,6 +94,17 @@ func newSimChain(duration uint32) *simChain {
s.blockHeight = -1
s.durationMask = duration
s.ttlSlices = make([][]Hash, s.durationMask+1)
s.rnd = rand.New(rand.NewSource(0))
return &s
}

// newSimChainWithSeed initializes and returns a simchain, with an externally supplied seed
func newSimChainWithSeed(duration uint32, seed int64) *simChain {
var s simChain
s.blockHeight = -1
s.durationMask = duration
s.ttlSlices = make([][]Hash, s.durationMask+1)
s.rnd = rand.New(rand.NewSource(seed))
return &s
}

Expand All @@ -108,11 +120,13 @@ func (s *simChain) BackOne(leaves []Leaf, durations []int32, dels []Hash) {
if durations[i] == 0 {
continue
}
fmt.Printf("removing %x at end of row %d\n", l.Hash[:4], durations[i])
// everything should be in order, right?
fmt.Printf("remove %x from end of ttl slice %d\n",
s.ttlSlices[durations[i]][len(s.ttlSlices[durations[i]])-1][:4],
durations[i])
if verbose {
fmt.Printf("removing %x at end of row %d\n", l.Hash[:4], durations[i])
// everything should be in order, right?
fmt.Printf("remove %x from end of ttl slice %d\n",
s.ttlSlices[durations[i]][len(s.ttlSlices[durations[i]])-1][:4],
durations[i])
}
s.ttlSlices[durations[i]] =
s.ttlSlices[durations[i]][:len(s.ttlSlices[durations[i]])-1]
}
Expand All @@ -137,7 +151,10 @@ func (s *simChain) ttlString() string {
// to be outputed
func (s *simChain) NextBlock(numAdds uint32) ([]Leaf, []int32, []Hash) {
s.blockHeight++
fmt.Printf("blockHeight %d\n", s.blockHeight)
if verbose {
fmt.Printf(
"blockHeight %d\n", s.blockHeight)
}

if s.blockHeight == 0 && numAdds == 0 {
numAdds = 1
Expand All @@ -160,7 +177,7 @@ func (s *simChain) NextBlock(numAdds uint32) ([]Leaf, []int32, []Hash) {
adds[j].Hash[4] = uint8(s.leafCounter >> 24)
adds[j].Hash[5] = uint8(s.leafCounter >> 32)

durations[j] = int32(rand.Uint32() & s.durationMask)
durations[j] = int32(s.rnd.Uint32() & s.durationMask)

// with "+1", the duration is 1 to 256, so the forest never gets
// big or tall. Without the +1, the duration is sometimes 0,
Expand Down
38 changes: 28 additions & 10 deletions accumulator/undo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"math/rand"
"reflect"
"testing"
)

Expand Down Expand Up @@ -149,21 +150,27 @@ func undoOnceRandom(blocks int32) error {

adds, durations, delHashes := sc.NextBlock(rand.Uint32() & 0x03)

fmt.Printf("\t\tblock %d del %d add %d - %s\n",
sc.blockHeight, len(delHashes), len(adds), f.Stats())
if verbose {
fmt.Printf("\t\tblock %d del %d add %d - %s\n",
sc.blockHeight, len(delHashes), len(adds), f.Stats())
}

bp, err := f.ProveBatch(delHashes)
if err != nil {
return err
}
beforeRoot := f.getRoots()
ub, err := f.Modify(adds, bp.Targets)
if err != nil {
return err
}
fmt.Print(f.ToString())
fmt.Print(sc.ttlString())
for h, p := range f.positionMap {
fmt.Printf("%x@%d ", h[:4], p)
if verbose {
fmt.Print(f.ToString())
fmt.Print(sc.ttlString())

for h, p := range f.positionMap {
fmt.Printf("%x@%d ", h[:4], p)
}
}
err = f.PosMapSanity()
if err != nil {
Expand All @@ -172,19 +179,30 @@ func undoOnceRandom(blocks int32) error {

//undo every 3rd block
if b%3 == 2 {
fmt.Print(ub.ToString())
if verbose {
fmt.Print(ub.ToString())
}
err := f.Undo(*ub)
if err != nil {
return err
}
fmt.Print("\n post undo map: ")
for h, p := range f.positionMap {
fmt.Printf("%x@%d ", h[:4], p)
if verbose {
fmt.Print("\n post undo map: ")
for h, p := range f.positionMap {
fmt.Printf("%x@%d ", h[:4], p)
}
}
sc.BackOne(adds, durations, delHashes)
afterRoot := f.getRoots()
if !reflect.DeepEqual(beforeRoot, afterRoot) {
return fmt.Errorf("undo mismatch")
}
}

}
if verbose {
fmt.Printf("\n")
}
return nil
}

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/adiabat/bech32 v0.0.0-20170505011816-6289d404861d
github.com/btcsuite/btcd v0.21.0-beta.0.20201124191514-610bb55ae85c
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce
github.com/dvyukov/go-fuzz v0.0.0-20210914135545-4980593459a1 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca
)

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/dvyukov/go-fuzz v0.0.0-20210914135545-4980593459a1 h1:YQOLTC8zvFaNSEuMexG0i7pY26bOksnQFsSJfGclo54=
github.com/dvyukov/go-fuzz v0.0.0-20210914135545-4980593459a1/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand Down

0 comments on commit cc944c1

Please sign in to comment.