Skip to content

Commit

Permalink
Merge pull request #306 from kcalvinalvin/undo-serialization
Browse files Browse the repository at this point in the history
accumulator/undo: Add serialization for undoblocks
  • Loading branch information
adiabat authored Aug 9, 2021
2 parents fc7a3c4 + dca867c commit 21ea8ec
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 7 deletions.
6 changes: 4 additions & 2 deletions accumulator/forest.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ func (f *Forest) addv2(adds []Leaf) {
// Note that this does not modify in place! All deletes occur simultaneous with
// adds, which show up on the right.
// Also, the deletes need there to be correct proof data, so you should first call Verify().
func (f *Forest) Modify(adds []Leaf, delsUn []uint64) (*undoBlock, error) {
func (f *Forest) Modify(adds []Leaf, delsUn []uint64) (*UndoBlock, error) {
numdels, numadds := len(delsUn), len(adds)
delta := int64(numadds - numdels) // watch 32/64 bit
if int64(f.numLeaves)+delta < 0 {
Expand Down Expand Up @@ -463,7 +463,9 @@ func (f *Forest) reMap(destRows uint8) error {
return fmt.Errorf("changing by more than 1 not programmed yet")
}

fmt.Printf("remap forest %d rows -> %d rows\n", f.rows, destRows)
if verbose {
fmt.Printf("remap forest %d rows -> %d rows\n", f.rows, destRows)
}

// for row reduction
if destRows < f.rows {
Expand Down
102 changes: 97 additions & 5 deletions accumulator/undo.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package accumulator

import (
"encoding/binary"
"fmt"
"io"
)

/* we need to be able to undo blocks! for bridge nodes at least.
Expand All @@ -13,14 +15,14 @@ although actually it can make sense for non-bridge nodes to undo as well...

// blockUndo is all the data needed to undo a block: number of adds,
// and all the hashes that got deleted and where they were from
type undoBlock struct {
type UndoBlock struct {
numAdds uint32 // number of adds in the block
positions []uint64 // position of all deletions this block
hashes []Hash // hashes that were deleted
}

// ToString returns a string
func (u *undoBlock) ToString() string {
func (u *UndoBlock) ToString() string {
s := fmt.Sprintf("- uuuu undo block %d adds\t", u.numAdds)
s += fmt.Sprintf("%d dels:\t", len(u.positions))
if len(u.positions) != len(u.hashes) {
Expand All @@ -34,8 +36,98 @@ func (u *undoBlock) ToString() string {
return s
}

// SerializeSize returns how many bytes it would take to serialize this undoblock.
func (u *UndoBlock) SerializeSize() int {
// Size of u.numAdds + len(u.positions) + each position takes up 8 bytes
size := 4 + 8 + (len(u.positions) * 8)

// Size of len(u.hashes) + each hash takes up 32 bytes
size += 8 + (len(u.hashes) * 32)

return size
}

// Serialize encodes the undoblock into the given writer.
func (u *UndoBlock) Serialize(w io.Writer) error {
err := binary.Write(w, binary.BigEndian, u.numAdds)
if err != nil {
return err
}

err = binary.Write(w, binary.BigEndian, uint64(len(u.positions)))
if err != nil {
return err
}

err = binary.Write(w, binary.BigEndian, u.positions)
if err != nil {
return err
}

err = binary.Write(w, binary.BigEndian, uint64(len(u.hashes)))
if err != nil {
return err
}

for _, hash := range u.hashes {
n, err := w.Write(hash[:])
if err != nil {
return err
}

if n != 32 {
err := fmt.Errorf("UndoBlock Serialize supposed to write 32 bytes but wrote %d bytes", n)
return err
}
}

return nil
}

// Deserialize decodes an undoblock from the reader.
func (u *UndoBlock) Deserialize(r io.Reader) error {
err := binary.Read(r, binary.BigEndian, &u.numAdds)
if err != nil {
return err
}

var posCount uint64
err = binary.Read(r, binary.BigEndian, &posCount)
if err != nil {
return err
}
u.positions = make([]uint64, posCount)

err = binary.Read(r, binary.BigEndian, u.positions)
if err != nil {
return err
}

var hashCount uint64
err = binary.Read(r, binary.BigEndian, &hashCount)
if err != nil {
return err
}

u.hashes = make([]Hash, hashCount)

for i := uint64(0); i < hashCount; i++ {
n, err := r.Read(u.hashes[i][:])
if err != nil {
return err
}

if n != 32 {
err := fmt.Errorf("UndoBlock Deserialize supposed to read 32 bytes but read %d bytes", n)
return err
}
}

return nil
}

// Undo reverts a Modify() with the given undoBlock.
func (f *Forest) Undo(ub undoBlock) error {
func (f *Forest) Undo(ub UndoBlock) error {
prevAdds := uint64(ub.numAdds)
prevDels := uint64(len(ub.hashes))
// how many leaves were there at the last block?
Expand Down Expand Up @@ -104,8 +196,8 @@ func (f *Forest) Undo(ub undoBlock) error {
}

// BuildUndoData makes an undoBlock from the same data that you'd give to Modify
func (f *Forest) BuildUndoData(numadds uint64, dels []uint64) *undoBlock {
ub := new(undoBlock)
func (f *Forest) BuildUndoData(numadds uint64, dels []uint64) *UndoBlock {
ub := new(UndoBlock)
ub.numAdds = uint32(numadds)

ub.positions = dels // the deletion positions, in sorted order
Expand Down
103 changes: 103 additions & 0 deletions accumulator/undo_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,114 @@
package accumulator

import (
"bytes"
"fmt"
"math/rand"
"testing"
)

func TestUndoSerializeDeserialize(t *testing.T) {
tests := []struct {
name string
undo UndoBlock
serialized []byte
}{
{
name: "testnet3 block 412",
undo: UndoBlock{
numAdds: 6,
positions: []uint64{455, 459, 461, 464},
hashes: []Hash{
Hash{
0x12, 0x53, 0x6b, 0x08, 0x63, 0x1b, 0x00, 0x3c,
0xa7, 0x97, 0xc3, 0x94, 0x33, 0x3b, 0x7f, 0x98,
0xd5, 0x4a, 0x7d, 0x36, 0x3c, 0x11, 0xc7, 0x44,
0x92, 0x60, 0x72, 0xa1, 0xad, 0xba, 0x20, 0x0b,
},
Hash{
0xf9, 0x0d, 0xea, 0x08, 0xc2, 0xcc, 0x6b, 0x71,
0x54, 0xe4, 0x38, 0x94, 0x40, 0xbc, 0x37, 0xc0,
0xc0, 0x99, 0x05, 0x11, 0x0d, 0x68, 0xd0, 0xf9,
0x57, 0x43, 0x0f, 0xac, 0x98, 0xe4, 0x59, 0x30,
},
Hash{
0xf0, 0xe2, 0xdc, 0x5e, 0xc0, 0x2e, 0x8f, 0x67,
0x32, 0xd4, 0x06, 0x4e, 0xc9, 0xb6, 0x73, 0x86,
0x62, 0xaf, 0xb0, 0x93, 0x69, 0xea, 0x9d, 0xfb,
0xa9, 0x05, 0x98, 0x11, 0x87, 0x05, 0x43, 0x85,
},
Hash{
0x88, 0x66, 0x53, 0xab, 0xd5, 0x16, 0x0f, 0xc5,
0x91, 0x9e, 0x0e, 0x38, 0x5c, 0x0b, 0x83, 0x7e,
0x97, 0xfb, 0xb9, 0x16, 0xed, 0x0a, 0x0d, 0xb6,
0x20, 0xba, 0x6b, 0xfc, 0x2b, 0xbb, 0x7c, 0xff,
},
},
},
},
{
name: "testnet3 block 450",
undo: UndoBlock{
numAdds: 3,
positions: []uint64{454, 474},
hashes: []Hash{
Hash{
0x10, 0xd1, 0x0a, 0xf8, 0xf2, 0x0b, 0x14, 0xc6,
0x12, 0xb1, 0x77, 0xa5, 0xaf, 0xd5, 0x44, 0x76,
0x0f, 0xf5, 0xc4, 0x96, 0x18, 0xac, 0x91, 0x1c,
0xa2, 0x43, 0xf4, 0x19, 0xf9, 0x31, 0xe1, 0x24,
},
Hash{
0x0a, 0xb5, 0x31, 0x83, 0xf6, 0x1f, 0xdb, 0xf0,
0xd1, 0x3d, 0x03, 0xd4, 0x4b, 0x44, 0x14, 0x0f,
0xc3, 0x89, 0x9a, 0x28, 0x25, 0xf6, 0xc5, 0x8e,
0xe7, 0x18, 0x69, 0x90, 0xe0, 0x85, 0xae, 0xfb,
},
},
},
},
}

for _, test := range tests {
beforeSize := test.undo.SerializeSize()
buf := make([]byte, 0, beforeSize)
w := bytes.NewBuffer(buf)
err := test.undo.Serialize(w)
if err != nil {
t.Fatal(err)
}

serializedBytes := w.Bytes()

beforeBytes := make([]byte, len(serializedBytes))
copy(beforeBytes, serializedBytes)

r := bytes.NewReader(serializedBytes)
afterUndo := new(UndoBlock)
afterUndo.Deserialize(r)

afterSize := afterUndo.SerializeSize()

if beforeSize != afterSize {
str := fmt.Errorf("Serialized sizes differ. Before:%dAfter:%d", beforeSize, afterSize)
t.Error(str)
}

afterBuf := make([]byte, 0, afterSize)
afterW := bytes.NewBuffer(afterBuf)
err = afterUndo.Serialize(afterW)
if err != nil {
t.Fatal(err)
}
afterBytes := afterW.Bytes()

if !bytes.Equal(beforeBytes[:], afterBytes[:]) {
str := fmt.Errorf("Serialized bytes differ. Before:%x after:%x", beforeBytes, afterBytes)
t.Error(str)
}
}
}

func TestUndoFixed(t *testing.T) {
rand.Seed(2)

Expand Down

0 comments on commit 21ea8ec

Please sign in to comment.