Skip to content

Commit

Permalink
accumulator/pollard: Pollard multiblockproof fix & utility functions (#…
Browse files Browse the repository at this point in the history
…338)

* accumulator/pollard: Bug fixes for pollard add.

The siblings of the added leaves will now also be set to be remembered.
This solves a bug where the sibling wasn't present when the proof ingest
doesn't happen before every single modify. This is now tested with the
test function TestPollardNoSiblingFound.

When a leaf hash to be remembered isn't found, the program paniced on a
nil pollard node. There is now a check to make sure the node is not nil
before trying to do operations on it.

* accumulator/pollardutil: Add pollard utility methods.

PrintRemembers(), PruneAll(), and NumLeaves() methods are added to the
pollard struct.
  • Loading branch information
kcalvinalvin authored Feb 14, 2022
1 parent df7b3aa commit 1ff40de
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 0 deletions.
7 changes: 7 additions & 0 deletions accumulator/pollard.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ func (p *Pollard) addOne(add Hash, remember bool) error {
leftRoot.niece, n.niece = n.niece, leftRoot.niece // swap
nHash := parentHash(leftRoot.data, n.data) // hash
n = &polNode{data: nHash, niece: [2]*polNode{leftRoot, n}} // new
n.remember = remember
p.hashesEver++

n.prune()
Expand Down Expand Up @@ -228,6 +229,12 @@ func (p *Pollard) rem2(dels []uint64) error {
if err != nil {
return err
}

if n == nil {
return fmt.Errorf("Cannot delete position %d err: %v",
del, ErrorStrings[ErrorNoPollardNode])
}

if n.remember == true {
p.currentRemember--
n.remember = false
Expand Down
59 changes: 59 additions & 0 deletions accumulator/pollard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,50 @@ import (
"testing"
)

func TestPollardNoSiblingFound(t *testing.T) {
var p Pollard

// Create the starting off pollard.
adds := make([]Leaf, 7)
for i := 0; i < len(adds); i++ {
adds[i].Hash[0] = uint8(i)
adds[i].Hash[20] = 0xff
adds[i].Remember = true
}
adds[6].Remember = false

err := p.Modify(adds, nil)
if err != nil {
t.Fatal(err)
}

// Create the next adds and dels that gets us to the stage right
// before the swapNodes error.
newAdds := make([]Leaf, 4)
for i := 0; i < len(newAdds); i++ {
newAdds[i].Hash[0] = uint8(i)
newAdds[i].Hash[1] = uint8(2)
newAdds[i].Hash[2] = uint8(7)
newAdds[i].Hash[20] = 0xff
}

newAdds[0].Remember = true
newAdds[1].Remember = true

dels := []uint64{2, 3, 4}
err = p.Modify(newAdds, dels)
if err != nil {
t.Fatal(err)
}

// Then cause the error by deleting 1,3,4,5
newDels := []uint64{1, 3, 4, 5}
err = p.Modify(nil, newDels)
if err != nil {
t.Fatal(err)
}
}

func TestPollardRand(t *testing.T) {
for z := 0; z < 30; z++ {
rand.Seed(int64(z))
Expand Down Expand Up @@ -220,6 +264,7 @@ func TestPollardIngestMultiBlockProof(t *testing.T) {
t.Fatal(fmt.Errorf("Couldn't prove the multi-block deletions. Error: %s",
err.Error()))
}
p.PruneAll()

// IngestBatchProof with rememberAll as true as all the proof here will be
// needed with later blocks.
Expand Down Expand Up @@ -479,6 +524,20 @@ func TestCache(t *testing.T) {
t.Fatal(err)
}
}

parent, _, _, err := p.readPos(pos)
if err != nil {
t.Fatal(fmt.Errorf("Couldn't read parent position of %d. err: %v", pos, err))
}

if l.Remember && parent == nil {
fmt.Println(p.ToString())
err := fmt.Errorf("leaf at position %d exists but it was added with "+
"remember=%v and its parent does not exist. "+
"polnode remember=%v",
pos, l.Remember, n.remember)
t.Fatal(err)
}
}
fmt.Println(p.ToString())
}
Expand Down
37 changes: 37 additions & 0 deletions accumulator/pollardutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package accumulator
import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"io"
)
Expand Down Expand Up @@ -59,6 +60,18 @@ func (n *polNode) printout() {
}
}

// PruneAll prunes the accumulator down to the roots.
func (p *Pollard) PruneAll() {
for _, root := range p.roots {
root.chop()
}
}

// NumLeaves returns the number of leaves that the accumulator has.
func (p *Pollard) NumLeaves() uint64 {
return p.numLeaves
}

// prune prunes deadend children.
// don't prune at the bottom; use leaf prune instead at row 1
func (n *polNode) prune() {
Expand Down Expand Up @@ -197,3 +210,27 @@ func (p *Pollard) Deserialize(serialized []byte) error {

return nil
}

// PrintRemembers prints all the nodes and their remember status. Useful for debugging.
func (p *Pollard) PrintRemembers() (string, error) {
str := ""

rows := p.rows()
// very naive loop looping outside the edge of the tree
for i := uint64(0); i < (2<<rows)-1; i++ {
// check if the leaf is within the tree
if !inForest(i, p.numLeaves, rows) {
continue
}
n, _, _, err := p.readPos(i)
if err != nil {
return "", err
}
if n != nil {
str += fmt.Sprintf("pos %d, data %s, remember %v\n",
i, hex.EncodeToString(n.data[:]), n.remember)
}
}

return str, nil
}

0 comments on commit 1ff40de

Please sign in to comment.