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(share/store_v2) Update file interface to use shwap types #3424

Closed
wants to merge 9 commits into from
Closed
17 changes: 0 additions & 17 deletions share/availability.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,12 @@ import (
"context"
"errors"

"github.com/celestiaorg/celestia-app/pkg/da"
"github.com/celestiaorg/rsmt2d"

"github.com/celestiaorg/celestia-node/header"
)

// ErrNotAvailable is returned whenever DA sampling fails.
var ErrNotAvailable = errors.New("share: data not available")

// Root represents root commitment to multiple Shares.
// In practice, it is a commitment to all the Data in a square.
type Root = da.DataAvailabilityHeader

// NewRoot generates Root(DataAvailabilityHeader) using the
// provided extended data square.
func NewRoot(eds *rsmt2d.ExtendedDataSquare) (*Root, error) {
dah, err := da.NewDataAvailabilityHeader(eds)
if err != nil {
return nil, err
}
return &dah, nil
}

// Availability defines interface for validation of Shares' availability.
//
//go:generate mockgen -destination=availability/mocks/availability.go -package=mocks . Availability
Expand Down
14 changes: 7 additions & 7 deletions share/eds/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,24 +121,24 @@ func CollectSharesByNamespace(
utils.SetStatusAndEnd(span, err)
}()

rootCIDs := ipld.FilterRootByNamespace(root, namespace)
if len(rootCIDs) == 0 {
rowIdxs := share.RowsWithNamespace(root, namespace)
if len(rowIdxs) == 0 {
return []share.NamespacedRow{}, nil
}

errGroup, ctx := errgroup.WithContext(ctx)
shares = make([]share.NamespacedRow, len(rootCIDs))
for i, rootCID := range rootCIDs {
shares = make([]share.NamespacedRow, len(rowIdxs))
for i, rowIdx := range rowIdxs {
// shadow loop variables, to ensure correct values are captured
i, rootCID := i, rootCID
i, rowRoot := i, root.RowRoots[rowIdx]
errGroup.Go(func() error {
row, proof, err := ipld.GetSharesByNamespace(ctx, bg, rootCID, namespace, len(root.RowRoots))
row, proof, err := ipld.GetSharesByNamespace(ctx, bg, rowRoot, namespace, len(root.RowRoots))
shares[i] = share.NamespacedRow{
Shares: row,
Proof: proof,
}
if err != nil {
return fmt.Errorf("retrieving shares by namespace %s for row %x: %w", namespace.String(), rootCID, err)
return fmt.Errorf("retrieving shares by namespace %s for row %d: %w", namespace.String(), i, err)
}
return nil
})
Expand Down
5 changes: 3 additions & 2 deletions share/ipld/get_shares.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,13 @@ func GetShares(ctx context.Context, bg blockservice.BlockGetter, root cid.Cid, s
func GetSharesByNamespace(
ctx context.Context,
bGetter blockservice.BlockGetter,
root cid.Cid,
root []byte,
namespace share.Namespace,
maxShares int,
) ([]share.Share, *nmt.Proof, error) {
rootCid := MustCidFromNamespacedSha256(root)
data := NewNamespaceData(maxShares, namespace, WithLeaves(), WithProofs())
err := data.CollectLeavesByNamespace(ctx, bGetter, root)
err := data.CollectLeavesByNamespace(ctx, bGetter, rootCid)
if err != nil {
return nil, nil, err
}
Expand Down
10 changes: 4 additions & 6 deletions share/ipld/get_shares_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,7 @@ func TestGetSharesByNamespace(t *testing.T) {
rowRoots, err := eds.RowRoots()
require.NoError(t, err)
for _, row := range rowRoots {
rcid := MustCidFromNamespacedSha256(row)
rowShares, _, err := GetSharesByNamespace(ctx, bServ, rcid, namespace, len(rowRoots))
rowShares, _, err := GetSharesByNamespace(ctx, bServ, row, namespace, len(rowRoots))
if errors.Is(err, ErrNamespaceOutsideRange) {
continue
}
Expand Down Expand Up @@ -363,8 +362,7 @@ func TestGetSharesWithProofsByNamespace(t *testing.T) {
rowRoots, err := eds.RowRoots()
require.NoError(t, err)
for _, row := range rowRoots {
rcid := MustCidFromNamespacedSha256(row)
rowShares, proof, err := GetSharesByNamespace(ctx, bServ, rcid, namespace, len(rowRoots))
rowShares, proof, err := GetSharesByNamespace(ctx, bServ, row, namespace, len(rowRoots))
if namespace.IsOutsideRange(row, row) {
require.ErrorIs(t, err, ErrNamespaceOutsideRange)
continue
Expand All @@ -386,15 +384,15 @@ func TestGetSharesWithProofsByNamespace(t *testing.T) {
share.NewSHA256Hasher(),
namespace.ToNMT(),
leaves,
NamespacedSha256FromCID(rcid))
row)
require.True(t, verified)

// verify inclusion
verified = proof.VerifyInclusion(
share.NewSHA256Hasher(),
namespace.ToNMT(),
rowShares,
NamespacedSha256FromCID(rcid))
row)
require.True(t, verified)
}
}
Expand Down
81 changes: 81 additions & 0 deletions share/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package share

import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"

Check failure on line 7 in share/root.go

View workflow job for this annotation

GitHub Actions / go-ci / Lint

File is not `gofumpt`-ed with `-extra` (gofumpt)
"github.com/celestiaorg/celestia-app/pkg/da"

Check failure on line 8 in share/root.go

View workflow job for this annotation

GitHub Actions / go-ci / Lint

File is not `goimports`-ed with -local github.com/celestiaorg/celestia-node (goimports)
"hash"

Check failure on line 9 in share/root.go

View workflow job for this annotation

GitHub Actions / go-ci / Lint

File is not `gofumpt`-ed with `-extra` (gofumpt)

"github.com/celestiaorg/rsmt2d"
)

// Root represents root commitment to multiple Shares.
// In practice, it is a commitment to all the Data in a square.
type Root = da.DataAvailabilityHeader

// NewRoot generates Root(DataAvailabilityHeader) using the
// provided extended data square.
func NewRoot(eds *rsmt2d.ExtendedDataSquare) (*Root, error) {
dah, err := da.NewDataAvailabilityHeader(eds)
if err != nil {
return nil, err
}
return &dah, nil
}

// DataHash is a representation of the Root hash.
type DataHash []byte

func (dh DataHash) Validate() error {
if len(dh) != 32 {
return fmt.Errorf("invalid hash size, expected 32, got %d", len(dh))
}
return nil
}

func (dh DataHash) String() string {
return fmt.Sprintf("%X", []byte(dh))
}

// IsEmptyRoot check whether DataHash corresponds to the root of an empty block EDS.
func (dh DataHash) IsEmptyRoot() bool {
return bytes.Equal(EmptyRoot().Hash(), dh)
}

// MustDataHashFromString converts a hex string to a valid datahash.
func MustDataHashFromString(datahash string) DataHash {
dh, err := hex.DecodeString(datahash)
if err != nil {
panic(fmt.Sprintf("datahash conversion: passed string was not valid hex: %s", datahash))
}
err = DataHash(dh).Validate()
if err != nil {
panic(fmt.Sprintf("datahash validation: passed hex string failed: %s", err))
}
return dh
}

// NewSHA256Hasher returns a new instance of a SHA-256 hasher.
func NewSHA256Hasher() hash.Hash {
return sha256.New()
}

// RootHashForCoordinates returns the root hash for the given coordinates.
func RootHashForCoordinates(r *Root, axisType rsmt2d.Axis, rowIdx, colIdx uint) []byte {
if axisType == rsmt2d.Row {
return r.RowRoots[rowIdx]
}
return r.ColumnRoots[colIdx]
}

// RowsWithNamespace returns indexes of rows, that might contain namespace.
func RowsWithNamespace(root *Root, namespace Namespace) (idxs []int) {
for i, row := range root.RowRoots {
if !namespace.IsOutsideRange(row, row) {
idxs = append(idxs, i)
}
}
return idxs
}
61 changes: 0 additions & 61 deletions share/share.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package share

import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"hash"

"github.com/celestiaorg/celestia-app/pkg/appconsts"
"github.com/celestiaorg/nmt"
Expand Down Expand Up @@ -72,61 +69,3 @@ func (s *ShareWithProof) Validate(rootHash []byte, x, y, edsSize int) bool {
rootHash,
)
}

// DataHash is a representation of the Root hash.
type DataHash []byte

func (dh DataHash) Validate() error {
if len(dh) != 32 {
return fmt.Errorf("invalid hash size, expected 32, got %d", len(dh))
}
return nil
}

func (dh DataHash) String() string {
return fmt.Sprintf("%X", []byte(dh))
}

// IsEmptyRoot check whether DataHash corresponds to the root of an empty block EDS.
func (dh DataHash) IsEmptyRoot() bool {
return bytes.Equal(EmptyRoot().Hash(), dh)
}

// MustDataHashFromString converts a hex string to a valid datahash.
func MustDataHashFromString(datahash string) DataHash {
dh, err := hex.DecodeString(datahash)
if err != nil {
panic(fmt.Sprintf("datahash conversion: passed string was not valid hex: %s", datahash))
}
err = DataHash(dh).Validate()
if err != nil {
panic(fmt.Sprintf("datahash validation: passed hex string failed: %s", err))
}
return dh
}

// NewSHA256Hasher returns a new instance of a SHA-256 hasher.
func NewSHA256Hasher() hash.Hash {
return sha256.New()
}

// RootHashForCoordinates returns the root hash for the given coordinates.
func RootHashForCoordinates(r *Root, axisType rsmt2d.Axis, rowIdx, colIdx uint) []byte {
if axisType == rsmt2d.Row {
return r.RowRoots[rowIdx]
}
return r.ColumnRoots[colIdx]
}

// FilterRootByNamespace returns the row roots from the given share.Root that contain the namespace.
// It also returns the half open range of the roots that contain the namespace.
func FilterRootByNamespace(root *Root, namespace Namespace) (rowRoots [][]byte, from, to int) {
for i, rowRoot := range root.RowRoots {
if !namespace.IsOutsideRange(rowRoot, rowRoot) {
rowRoots = append(rowRoots, rowRoot)
to = i
}
}
to++
return rowRoots, to - len(rowRoots), to
}
17 changes: 8 additions & 9 deletions share/shwap/namespaced_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,17 @@ func (ns NamespacedData) Flatten() []share.Share {

// Verify checks the integrity of the NamespacedData against a provided root and namespace.
func (ns NamespacedData) Verify(root *share.Root, namespace share.Namespace) error {
rowRoots, _, _ := share.FilterRootByNamespace(root, namespace)
rowIdxs := share.RowsWithNamespace(root, namespace)

if len(rowRoots) != len(ns) {
return fmt.Errorf("expected %d rows, found %d rows", len(rowRoots), len(ns))
if len(rowIdxs) != len(ns) {
return fmt.Errorf("expected %d rows, found %d rows", len(rowIdxs), len(ns))
}

for i, row := range ns {
if row.Proof == nil || len(row.Shares) == 0 {
return fmt.Errorf("row %d is missing proofs or shares", i)
}
if !row.VerifyInclusion(rowRoots[i], namespace) {
return fmt.Errorf("failed to verify row %d", i)
for i, rowIdx := range rowIdxs {
row := ns[i]
err := row.Validate(root, namespace, rowIdx)
if err != nil {
return fmt.Errorf("failed to validate row %d: %w", i, err)
}
}
return nil
Expand Down
12 changes: 6 additions & 6 deletions share/shwap/namespaced_data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,13 @@ func TestValidateNamespacedRow(t *testing.T) {
require.NoError(t, err)
require.True(t, len(nd) > 0)

_, from, to := share.FilterRootByNamespace(root, namespace)
require.Len(t, nd, to-from)
idx := from
for _, row := range nd {
err = row.Validate(root, namespace, idx)
rowidxs := share.RowsWithNamespace(root, namespace)
require.Equal(t, len(rowidxs), len(nd))

for i, rowidx := range rowidxs {
row := nd[i]
err = row.Validate(root, namespace, rowidx)
require.NoError(t, err)
idx++
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions share/store/file/axis_half.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package file

import (
"github.com/celestiaorg/celestia-node/share"
"github.com/celestiaorg/celestia-node/share/shwap"
)

type AxisHalf struct {
Shares []share.Share
IsParity bool
}

func (a AxisHalf) ToRow() shwap.Row {
side := shwap.Left
if a.IsParity {
side = shwap.Right
}
return shwap.NewRow(a.Shares, side)
}
11 changes: 6 additions & 5 deletions share/store/eds_file.go → share/store/file/eds_file.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package store
package file

import (
"context"
Expand All @@ -7,18 +7,19 @@ import (
"github.com/celestiaorg/rsmt2d"

"github.com/celestiaorg/celestia-node/share"
"github.com/celestiaorg/celestia-node/share/shwap"
)

type EdsFile interface {
io.Closer
// Size returns square size of the file.
Size() int
// Share returns share and corresponding proof for the given axis and share index in this axis.
Share(ctx context.Context, x, y int) (*share.ShareWithProof, error)
// AxisHalf returns shares for the first half of the axis of the given type and index.
AxisHalf(ctx context.Context, axisType rsmt2d.Axis, axisIdx int) ([]share.Share, error)
Share(ctx context.Context, rowIdx, colIdx int) (*shwap.Sample, error)
// AxisHalf returns Shares for the first half of the axis of the given type and index.
AxisHalf(ctx context.Context, axisType rsmt2d.Axis, axisIdx int) (AxisHalf, error)
// Data returns data for the given namespace and row index.
Data(ctx context.Context, namespace share.Namespace, rowIdx int) (share.NamespacedRow, error)
Data(ctx context.Context, namespace share.Namespace, rowIdx int) (shwap.RowNamespaceData, error)
// EDS returns extended data square stored in the file.
EDS(ctx context.Context) (*rsmt2d.ExtendedDataSquare, error)
}
Loading
Loading