Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 20 additions & 41 deletions datasquare.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
package rsmt2d

import (
"crypto/sha256"
"errors"
"hash"
"math"

"github.com/NebulousLabs/merkletree"
)

type dataSquare struct {
square [][][]byte
width uint
chunkSize uint
rowRoots [][]byte
columnRoots [][]byte
hasher hash.Hash
square [][][]byte
width uint
chunkSize uint
rowRoots [][]byte
columnRoots [][]byte
createTreeFn TreeConstructorFn
}

func newDataSquare(data [][]byte) (*dataSquare, error) {
func newDataSquare(data [][]byte, treeCreator TreeConstructorFn) (*dataSquare, error) {
width := int(math.Ceil(math.Sqrt(float64(len(data)))))
if int(math.Pow(float64(width), 2)) != len(data) {
return nil, errors.New("number of chunks must be a square number")
Expand All @@ -37,18 +33,13 @@ func newDataSquare(data [][]byte) (*dataSquare, error) {
}

return &dataSquare{
square: square,
width: uint(width),
chunkSize: uint(chunkSize),
hasher: sha256.New(),
square: square,
width: uint(width),
chunkSize: uint(chunkSize),
createTreeFn: treeCreator,
}, nil
}

// SetHasher sets the hasher used for computing Merkle roots.
func (ds *dataSquare) SetHasher(hasher hash.Hash) {
ds.hasher = hasher
}

func (ds *dataSquare) extendSquare(extendedWidth uint, fillerChunk []byte) error {
if uint(len(fillerChunk)) != ds.chunkSize {
return errors.New("filler chunk size does not match data square chunk size")
Expand Down Expand Up @@ -149,15 +140,11 @@ func (ds *dataSquare) resetRoots() {
func (ds *dataSquare) computeRoots() {
rowRoots := make([][]byte, ds.width)
columnRoots := make([][]byte, ds.width)
var rowTree *merkletree.Tree
var columnTree *merkletree.Tree
var rowData [][]byte
var columnData [][]byte
for i := uint(0); i < ds.width; i++ {
rowTree = merkletree.New(ds.hasher)
columnTree = merkletree.New(ds.hasher)
rowData = ds.Row(i)
columnData = ds.Column(i)
rowTree := ds.createTreeFn()
columnTree := ds.createTreeFn()
rowData := ds.Row(i)
columnData := ds.Column(i)
for j := uint(0); j < ds.width; j++ {
rowTree.Push(rowData[j])
columnTree.Push(columnData[j])
Expand Down Expand Up @@ -190,34 +177,26 @@ func (ds *dataSquare) ColumnRoots() [][]byte {
}

func (ds *dataSquare) computeRowProof(x uint, y uint) ([]byte, [][]byte, uint, uint, error) {
tree := merkletree.New(ds.hasher)
err := tree.SetIndex(uint64(y))
if err != nil {
return nil, nil, 0, 0, err
}
tree := ds.createTreeFn()
data := ds.Row(x)

for i := uint(0); i < ds.width; i++ {
tree.Push(data[i])
}

merkleRoot, proof, proofIndex, numLeaves := tree.Prove()
merkleRoot, proof, proofIndex, numLeaves := tree.Prove(int(y))
return merkleRoot, proof, uint(proofIndex), uint(numLeaves), nil
}

func (ds *dataSquare) computeColumnProof(x uint, y uint) ([]byte, [][]byte, uint, uint, error) {
tree := merkletree.New(ds.hasher)
err := tree.SetIndex(uint64(x))
if err != nil {
return nil, nil, 0, 0, err
}
tree := ds.createTreeFn()
data := ds.Column(y)

for i := uint(0); i < ds.width; i++ {
tree.Push(data[i])
}

merkleRoot, proof, proofIndex, numLeaves := tree.Prove()
// TODO(ismail): check for overflow when casting from uint -> int
merkleRoot, proof, proofIndex, numLeaves := tree.Prove(int(x))
return merkleRoot, proof, uint(proofIndex), uint(numLeaves), nil
}

Expand Down
18 changes: 9 additions & 9 deletions datasquare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,35 @@ import (
)

func TestNewDataSquare(t *testing.T) {
result, err := newDataSquare([][]byte{{1, 2}})
result, err := newDataSquare([][]byte{{1, 2}}, NewDefaultTree)
if err != nil {
panic(err)
}
if !reflect.DeepEqual(result.square, [][][]byte{{{1, 2}}}) {
t.Errorf("newDataSquare failed for 1x1 square")
}

result, err = newDataSquare([][]byte{{1, 2}, {3, 4}, {5, 6}, {7, 8}})
result, err = newDataSquare([][]byte{{1, 2}, {3, 4}, {5, 6}, {7, 8}}, NewDefaultTree)
if err != nil {
panic(err)
}
if !reflect.DeepEqual(result.square, [][][]byte{{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}}) {
t.Errorf("newDataSquare failed for 2x2 square")
}

_, err = newDataSquare([][]byte{{1, 2}, {3, 4}, {5, 6}})
_, err = newDataSquare([][]byte{{1, 2}, {3, 4}, {5, 6}}, NewDefaultTree)
if err == nil {
t.Errorf("newDataSquare failed; inconsistent number of chunks accepted")
}

_, err = newDataSquare([][]byte{{1, 2}, {3, 4}, {5, 6}, {7}})
_, err = newDataSquare([][]byte{{1, 2}, {3, 4}, {5, 6}, {7}}, NewDefaultTree)
if err == nil {
t.Errorf("newDataSquare failed; chunks of unequal size accepted")
}
}

func TestExtendSquare(t *testing.T) {
ds, err := newDataSquare([][]byte{{1, 2}})
ds, err := newDataSquare([][]byte{{1, 2}}, NewDefaultTree)
if err != nil {
panic(err)
}
Expand All @@ -43,7 +43,7 @@ func TestExtendSquare(t *testing.T) {
t.Errorf("extendSquare failed; error not returned when filler chunk size does not match data square chunk size")
}

ds, err = newDataSquare([][]byte{{1, 2}})
ds, err = newDataSquare([][]byte{{1, 2}}, NewDefaultTree)
if err != nil {
panic(err)
}
Expand All @@ -57,7 +57,7 @@ func TestExtendSquare(t *testing.T) {
}

func TestRoots(t *testing.T) {
result, err := newDataSquare([][]byte{{1, 2}})
result, err := newDataSquare([][]byte{{1, 2}}, NewDefaultTree)
if err != nil {
panic(err)
}
Expand All @@ -67,7 +67,7 @@ func TestRoots(t *testing.T) {
}

func TestProofs(t *testing.T) {
result, err := newDataSquare([][]byte{{1, 2}, {3, 4}, {5, 6}, {7, 8}})
result, err := newDataSquare([][]byte{{1, 2}, {3, 4}, {5, 6}, {7, 8}}, NewDefaultTree)
if err != nil {
panic(err)
}
Expand All @@ -85,7 +85,7 @@ func TestProofs(t *testing.T) {
t.Errorf("computing row proof for (1, 1) in 2x2 square failed; expecting number of leaves to be 2")
}

result, err = newDataSquare([][]byte{{1, 2}, {3, 4}, {5, 6}, {7, 8}})
result, err = newDataSquare([][]byte{{1, 2}, {3, 4}, {5, 6}, {7, 8}}, NewDefaultTree)
if err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion extendeddatacrossword.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func RepairExtendedDataSquare(rowRoots [][]byte, columnRoots [][]byte, data [][]
}
}

eds, err := ImportExtendedDataSquare(data, codec)
eds, err := ImportExtendedDataSquare(data, codec, NewDefaultTree)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion extendeddatacrossword_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestRepairExtendedDataSquare(t *testing.T) {
original, err := ComputeExtendedDataSquare([][]byte{
ones, twos,
threes, fours,
}, codec)
}, codec, NewDefaultTree)
if err != nil {
panic(err)
}
Expand Down
10 changes: 5 additions & 5 deletions extendeddatasquare.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type ExtendedDataSquare struct {
}

// ComputeExtendedDataSquare computes the extended data square for some chunks of data.
func ComputeExtendedDataSquare(data [][]byte, codecType CodecType) (*ExtendedDataSquare, error) {
func ComputeExtendedDataSquare(data [][]byte, codecType CodecType, treeCreatorFn TreeConstructorFn) (*ExtendedDataSquare, error) {
if codec, ok := codecs[codecType]; !ok {
return nil, errors.New("unsupported codecType")
} else {
Expand All @@ -23,7 +23,7 @@ func ComputeExtendedDataSquare(data [][]byte, codecType CodecType) (*ExtendedDat
}
}

ds, err := newDataSquare(data)
ds, err := newDataSquare(data, treeCreatorFn)
if err != nil {
return nil, err
}
Expand All @@ -38,15 +38,15 @@ func ComputeExtendedDataSquare(data [][]byte, codecType CodecType) (*ExtendedDat
}

// ImportExtendedDataSquare imports an extended data square, represented as flattened chunks of data.
func ImportExtendedDataSquare(data [][]byte, codecType CodecType) (*ExtendedDataSquare, error) {
func ImportExtendedDataSquare(data [][]byte, codecType CodecType, treeCreatorFn TreeConstructorFn) (*ExtendedDataSquare, error) {
if codec, ok := codecs[codecType]; !ok {
return nil, errors.New("unsupported codecType")
} else {
if len(data) > 4*codec.maxChunks() {
return nil, errors.New("number of chunks exceeds the maximum")
}
}
ds, err := newDataSquare(data)
ds, err := newDataSquare(data, treeCreatorFn)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -125,6 +125,6 @@ func (eds *ExtendedDataSquare) erasureExtendSquare() error {
}

func (eds *ExtendedDataSquare) deepCopy() (ExtendedDataSquare, error) {
eds, err := ImportExtendedDataSquare(eds.flattened(), eds.codec)
eds, err := ImportExtendedDataSquare(eds.flattened(), eds.codec, eds.createTreeFn)
return *eds, err
}
2 changes: 1 addition & 1 deletion extendeddatasquare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ func TestComputeExtendedDataSquare(t *testing.T) {
result, err := ComputeExtendedDataSquare([][]byte{
{1}, {2},
{3}, {4},
}, codec)
}, codec, NewDefaultTree)
if err != nil {
panic(err)
}
Expand Down
4 changes: 1 addition & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ module github.com/lazyledger/rsmt2d
go 1.14

require (
github.com/NebulousLabs/errors v0.0.0-20181203160057-9f787ce8f69e // indirect
github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e // indirect
github.com/NebulousLabs/merkletree v0.0.0-20181203152040-08d5d54b07f5
github.com/davecgh/go-spew v1.1.1 // indirect
// only needed if built with go build -tags leopard
github.com/lazyledger/go-leopard v0.0.0-20200604113236-298f93361181
github.com/lazyledger/merkletree v0.0.0-20201214195110-6901c4c3c75f
github.com/spacemonkeygo/errors v0.0.0-20171212215202-9064522e9fd1 // indirect
github.com/stretchr/testify v1.5.1
github.com/vivint/infectious v0.0.0-20190108171102-2455b059135b
Expand Down
16 changes: 10 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
github.com/NebulousLabs/errors v0.0.0-20181203160057-9f787ce8f69e h1:9UwdEr0AFI021vXG+hXDGH9ZHGuNtPCdJkRTqcIhJ3A=
github.com/NebulousLabs/errors v0.0.0-20181203160057-9f787ce8f69e/go.mod h1:J7tUI9Fg4YuFLsqeLE5uIp93Fot9oBCw2vwZJvLmWso=
github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e h1:n+DcnTNkQnHlwpsrHoQtkrJIO7CBx029fw6oR4vIob4=
github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e/go.mod h1:Bdzq+51GR4/0DIhaICZEOm+OHvXGwwB2trKZ8B4Y6eQ=
github.com/NebulousLabs/merkletree v0.0.0-20181203152040-08d5d54b07f5 h1:pk9SclNGplPbF6YDIDKMhHh9SaUWcoxPkMr7zdu1hfk=
github.com/NebulousLabs/merkletree v0.0.0-20181203152040-08d5d54b07f5/go.mod h1:Cn056wBLKay+uIS9LJn7ymwhgC5mqbOtG6iOhEvyy4M=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand All @@ -13,6 +7,8 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGw
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/lazyledger/go-leopard v0.0.0-20200604113236-298f93361181 h1:mUeCGuCgjZVadW4CzA2dMBq7p2BqaoCfpnKjxMmSaSE=
github.com/lazyledger/go-leopard v0.0.0-20200604113236-298f93361181/go.mod h1:v1o1CRihQ9i7hizx23KK4aR79lxA6VDUIzUCfDva0XQ=
github.com/lazyledger/merkletree v0.0.0-20201214195110-6901c4c3c75f h1:jbyPAH6o6hGte4RtZBaqWs2n4Fl6hS7qJGXX3qnjiy4=
github.com/lazyledger/merkletree v0.0.0-20201214195110-6901c4c3c75f/go.mod h1:10PA0NlnYtB8HrtwIDQAyTKWp8TEZ0zBZCGlYC/7+QE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spacemonkeygo/errors v0.0.0-20171212215202-9064522e9fd1 h1:xHQewZjohU9/wUsyC99navCjQDNHtTgUOM/J1jAbzfw=
Expand All @@ -22,16 +18,24 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/vivint/infectious v0.0.0-20190108171102-2455b059135b h1:dLkqBELopfQNhe8S9ucnSf+HhiUCgK/hPIjVG0f9GlY=
github.com/vivint/infectious v0.0.0-20190108171102-2455b059135b/go.mod h1:5oyMAv4hrBEKqBwORFsiqIrCNCmL2qcZLQTdJLYeYIc=
gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8 h1:gZfMjx7Jr6N8b7iJO4eUjDsn6xJqoyXg8D+ogdoAfKY=
gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8/go.mod h1:ZkMZ0dpQyWwlENaeZVBiQRjhMEZvk6VTXquzl3FOFP8=
gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40 h1:dizWJqTWjwyD8KGcMOwgrkqu1JIkofYgKkmDeNE7oAs=
gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40/go.mod h1:rOnSnoRyxMI3fe/7KIbVcsHRGxe30OONv8dEgo+vCfA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 h1:nVJ3guKA9qdkEQ3TUdXI9QSINo2CUPM/cySEvw2w8I0=
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2 h1:y102fOLFqhV41b+4GPiJoa0k/x+pJcEi2/HB1Y5T6fU=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down
57 changes: 57 additions & 0 deletions tree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package rsmt2d

import (
"crypto/sha256"
"fmt"

"github.com/lazyledger/merkletree"
)

// TreeConstructorFn creates a fresh Tree instance to be used as the Merkle inside of rsmt2d.
type TreeConstructorFn = func() Tree

type Tree interface {
Push(data []byte)
// TODO(ismail): is this general enough?
Prove(idx int) (merkleRoot []byte, proofSet [][]byte, proofIndex uint64, numLeaves uint64)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Root() []byte
}
Comment on lines +13 to +18
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't merge yet. Trying to distill a minimal interface. This one is basically nebolouslabs' API (and these are the methods used).


var _ Tree = &DefaultTree{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would developers use their own tree implementation for the library? Create a struct that implements Tree, then supply a function that creates a new tree?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a struct that implements Tree, then supply a function that creates a new tree?

Yes, exactly.


type DefaultTree struct {
*merkletree.Tree
leaves [][]byte
root []byte
}

func NewDefaultTree() Tree {
return &DefaultTree{
Tree: merkletree.New(sha256.New()),
leaves: make([][]byte, 0, 128),
}
}

func (d *DefaultTree) Push(data []byte) {
d.leaves = append(d.leaves, data)
}

func (d *DefaultTree) Prove(idx int) (merkleRoot []byte, proofSet [][]byte, proofIndex uint64, numLeaves uint64) {
if err := d.Tree.SetIndex(uint64(idx)); err != nil {
panic(fmt.Sprintf("don't call prove on a already used tree: %v", err))
}
for _, l := range d.leaves {
d.Tree.Push(l)
}
return d.Tree.Prove()
}

func (d *DefaultTree) Root() []byte {
if d.root == nil {
for _, l := range d.leaves {
d.Tree.Push(l)
}
d.root = d.Tree.Root()
}
return d.root
}