Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: return an error for RowRoots and ColRoots if EDS incomplete #213

Merged
merged 11 commits into from
Aug 4, 2023
32 changes: 26 additions & 6 deletions datasquare.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,15 +244,20 @@ func (ds *dataSquare) getRowRoots() ([][]byte, error) {
return ds.rowRoots, nil
}

// getRowRoot calculates and returns the root of the selected row. Note: unlike the
// getRowRoots method, getRowRoot does not write to the built-in cache.
// getRowRoot calculates and returns the root of the selected row. Note: unlike
// the getRowRoots method, getRowRoot does not write to the built-in cache.
// Returns an error if the row is incomplete (i.e. some shares are nil).
func (ds *dataSquare) getRowRoot(x uint) ([]byte, error) {
if ds.rowRoots != nil {
return ds.rowRoots[x], nil
}

tree := ds.createTreeFn(Row, x)
for _, d := range ds.row(x) {
row := ds.row(x)
if !isComplete(row) {
return nil, errors.New("can not compute root of incomplete row")
}
for _, d := range row {
err := tree.Push(d)
if err != nil {
return nil, err
Expand All @@ -274,15 +279,20 @@ func (ds *dataSquare) getColRoots() ([][]byte, error) {
return ds.colRoots, nil
}

// getColRoot calculates and returns the root of the selected row. Note: unlike the
// getColRoots method, getColRoot does not write to the built-in cache.
// getColRoot calculates and returns the root of the selected row. Note: unlike
// the getColRoots method, getColRoot does not write to the built-in cache.
// Returns an error if the column is incomplete (i.e. some shares are nil).
func (ds *dataSquare) getColRoot(y uint) ([]byte, error) {
if ds.colRoots != nil {
return ds.colRoots[y], nil
}

tree := ds.createTreeFn(Col, y)
for _, d := range ds.col(y) {
col := ds.col(y)
if !isComplete(col) {
return nil, errors.New("can not compute root of incomplete column")
}
for _, d := range col {
err := tree.Push(d)
if err != nil {
return nil, err
Expand Down Expand Up @@ -326,3 +336,13 @@ func (ds *dataSquare) Flattened() [][]byte {

return flattened
}

// isComplete returns true if all the shares are non-nil.
func isComplete(shares [][]byte) bool {
for _, share := range shares {
if share == nil {
return false
}
}
return true
}
14 changes: 9 additions & 5 deletions extendeddatacrossword_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,17 +228,21 @@ func TestCorruptedEdsReturnsErrByzantineData(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
eds := createTestEds(codec, shareSize)
for i, coords := range test.coords {
x := coords[0]
y := coords[1]
eds.setCell(x, y, test.values[i])
}

// compute the rowRoots prior to corruption
rowRoots, err := eds.getRowRoots()
assert.NoError(t, err)

// compute the colRoots prior to corruption
colRoots, err := eds.getColRoots()
assert.NoError(t, err)

for i, coords := range test.coords {
x := coords[0]
y := coords[1]
eds.setCell(x, y, test.values[i])
}

err = eds.Repair(rowRoots, colRoots)
assert.Error(t, err)

Expand Down
6 changes: 4 additions & 2 deletions extendeddatasquare.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,8 @@ func (eds *ExtendedDataSquare) Col(y uint) [][]byte {
return deepCopy(eds.col(y))
}

// ColRoots returns the Merkle roots of all the columns in the square.
// ColRoots returns the Merkle roots of all the columns in the square. Returns
// an error if the EDS is incomplete (i.e. some shares are nil).
func (eds *ExtendedDataSquare) ColRoots() ([][]byte, error) {
colRoots, err := eds.getColRoots()
if err != nil {
Expand All @@ -251,7 +252,8 @@ func (eds *ExtendedDataSquare) Row(x uint) [][]byte {
return deepCopy(eds.row(x))
}

// RowRoots returns the Merkle roots of all the rows in the square.
// RowRoots returns the Merkle roots of all the rows in the square. Returns an
// error if the EDS is incomplete (i.e. some shares are nil).
func (eds *ExtendedDataSquare) RowRoots() ([][]byte, error) {
rowRoots, err := eds.getRowRoots()
if err != nil {
Expand Down
56 changes: 56 additions & 0 deletions extendeddatasquare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,62 @@ func TestEDSRowColImmutable(t *testing.T) {
}
}

func TestRowRoots(t *testing.T) {
t.Run("returns row roots for a 4x4 EDS", func(t *testing.T) {
eds, err := ComputeExtendedDataSquare([][]byte{
ones, twos,
threes, fours,
}, NewLeoRSCodec(), NewDefaultTree)
require.NoError(t, err)

rowRoots, err := eds.RowRoots()
assert.NoError(t, err)
assert.Len(t, rowRoots, 4)
})

t.Run("returns an error for an incomplete EDS", func(t *testing.T) {
eds, err := ComputeExtendedDataSquare([][]byte{
ones, twos,
threes, fours,
}, NewLeoRSCodec(), NewDefaultTree)
require.NoError(t, err)

// set a cell to nil to make the EDS incomplete
eds.setCell(0, 0, nil)

_, err = eds.RowRoots()
assert.Error(t, err)
})
}

func TestColRoots(t *testing.T) {
t.Run("returns col roots for a 4x4 EDS", func(t *testing.T) {
eds, err := ComputeExtendedDataSquare([][]byte{
ones, twos,
threes, fours,
}, NewLeoRSCodec(), NewDefaultTree)
require.NoError(t, err)

colRoots, err := eds.ColRoots()
assert.NoError(t, err)
assert.Len(t, colRoots, 4)
})

t.Run("returns an error for an incomplete EDS", func(t *testing.T) {
eds, err := ComputeExtendedDataSquare([][]byte{
ones, twos,
threes, fours,
}, NewLeoRSCodec(), NewDefaultTree)
require.NoError(t, err)

// set a cell to nil to make the EDS incomplete
eds.setCell(0, 0, nil)

_, err = eds.ColRoots()
assert.Error(t, err)
})
}

// dump acts as a data dump for the benchmarks to stop the compiler from making
// unrealistic optimizations
var dump *ExtendedDataSquare
Expand Down
Loading