Skip to content

feat(shwap/blob): add blob to shwap#4765

Open
vgonkivs wants to merge 6 commits intomainfrom
add_blob_over_shrex
Open

feat(shwap/blob): add blob to shwap#4765
vgonkivs wants to merge 6 commits intomainfrom
add_blob_over_shrex

Conversation

@vgonkivs
Copy link
Member

@vgonkivs vgonkivs commented Jan 22, 2026

The PR adds a support of Blob type request and response for the shrex/protocol. Please note that the PR is breaking as it breaks the proof verification logic: instead of proving the whole namespace within the Row, blob Proof now tied to the specific blob.

@github-actions github-actions bot added the kind:break! Attached to breaking PRs label Jan 22, 2026
@vgonkivs vgonkivs self-assigned this Jan 23, 2026
@vgonkivs vgonkivs force-pushed the add_blob_over_shrex branch 6 times, most recently from 05f41a8 to f6bf9f1 Compare January 25, 2026 08:55
@vgonkivs vgonkivs marked this pull request as ready for review January 25, 2026 09:05
@vgonkivs vgonkivs changed the title add blob to shwap feat!(shwap/blob): add blob to shwap Jan 26, 2026
@vgonkivs vgonkivs added the kind:feat Attached to feature PRs label Jan 26, 2026
@vgonkivs vgonkivs changed the title feat!(shwap/blob): add blob to shwap feat(shwap/blob): add blob to shwap Feb 9, 2026
@vgonkivs vgonkivs removed the kind:break! Attached to breaking PRs label Feb 9, 2026
Copy link
Member

@renaynay renaynay left a comment

Choose a reason for hiding this comment

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

Some questions:

  1. what is the point of GetBlobs to be implemented over blob over shwap -- why wouldn't it just route to namespace data over shwap? Is this just an optimisation not to send padding if it exists? If so, then what's the point of shares by namespace over shwap (besides namespace non-inclusion)?
  2. similarly, GetBlobs() feels to me like it should be a message at shwap protocol level for fetching multiple blobs by commitment rather than just all -- right now, we can only fetch 1 blob by commitment over shwap or fetching ALL blobs in that namespace. Is there a case that we would need that? fetching multiple blobs by commitment from the same namespace? And not to bikeshed, but similarly, would there be a need to fetch blobs from different namespaces but at the same height such that it would be more efficient to batch it in one request?
  3. Can we DRY between Blob()/Blobs() methods between rsmt2d.go and ods.go
  4. Why do we need two methods on accessor? Blob + Blobs? Can we not just have 1 (Blobs) and pass in 1 or multiple there? Right now, this is probably b/c we have the binary 1 blob or ALL blobs.
  5. feels like missing lots of unit tests

ctx, span := tracer.Start(
ctx,
"cascade/get-blob",
trace.WithAttributes(
Copy link
Member

Choose a reason for hiding this comment

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

add commitment here

return shwap.BlobFromShares(extendedRowShares, namespace, commitment, odsSize)
}

func (eds *Rsmt2D) Blobs(ctx context.Context, namespace libshare.Namespace) ([]*shwap.Blob, error) {
Copy link
Member

Choose a reason for hiding this comment

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

where are unit tests for these methods

Copy link
Member Author

Choose a reason for hiding this comment

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

added

commitment []byte,
) (*shwap.Blob, error) {
var err error
ctx, span := tracer.Start(ctx, "shrex/get-blob", trace.WithAttributes(
Copy link
Member

Choose a reason for hiding this comment

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

we need to pull span from the trace that's passed through cascade getter rather than starting new one

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the problem of all methods in shrex. Probably, I missed this part during shrex refactoring. I'll prepare a PR to fix all at once.

ns libshare.Namespace,
) ([]*shwap.Blob, error) {
var err error
ctx, span := tracer.Start(ctx, "shrex/get-blobs", trace.WithAttributes(
Copy link
Member

Choose a reason for hiding this comment

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

same

return errors.New("nil response")
}
for _, blob := range response {
err = blob.VerifyInclusion(header.DAH.RowRoots)
Copy link
Member

Choose a reason for hiding this comment

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

why are we only calling VerifyInclusion here instead of top-level Verify?

Copy link
Member Author

Choose a reason for hiding this comment

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

We don't know the commitments during GetBlobs. So we are interested only in inclusion.

}, nil
}

func BlobsFromShares(
Copy link
Member

Choose a reason for hiding this comment

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

Was all of this code moved from the blob pkg down here?

Copy link
Member Author

Choose a reason for hiding this comment

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

no


// BlobIDSize defines the total size of a BlobID in bytes, combining the
// size of a EdsID, the size of a Namespace and the commitment size
const BlobIDSize = EdsIDSize + libshare.NamespaceSize + commitmentSize
Copy link
Member

Choose a reason for hiding this comment

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

is there any test coverage for this file?

func (blbID *BlobID) ResponseReader(ctx context.Context, acc Accessor) (io.Reader, error) {
buf := &bytes.Buffer{}

if !bytes.Equal(blbID.Commitment, emptyCommitment) {
Copy link
Member

Choose a reason for hiding this comment

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

Switch case on here to make more readable?


// Make a copy of the outer slice to avoid modifying the caller's slice
// when we reassign row elements below.
shares := make([][]libshare.Share, len(extendedRowShares))
Copy link
Member

Choose a reason for hiding this comment

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

can we extract into separate PR as this is a big feature PR and would rather not bundle other stuff into

Copy link
Member Author

@vgonkivs vgonkivs Feb 16, 2026

Choose a reason for hiding this comment

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

This change is necessary and imo should be a part of this PR. Previously, a single range was requested which allows us to modify the outer slice. In case of blob's retrieval it will lead to an error with indexing, out of bounds errors etc.

return shwap.RangeNamespaceDataFromShares(shares, fromCoords, toCoords)
}

func (o *ODS) Blob(ctx context.Context, namespace libshare.Namespace, commitment []byte) (*shwap.Blob, error) {
Copy link
Member

Choose a reason for hiding this comment

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

where are units?

Copy link
Member Author

Choose a reason for hiding this comment

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

already working on them

@vgonkivs
Copy link
Member Author

what is the point of GetBlobs to be implemented over blob over shwap -- why wouldn't it just route to namespace data over shwap? Is this just an optimisation not to send padding if it exists? If so, then what's the point of shares by namespace over shwap (besides namespace non-inclusion)?

The point is to have a proof, associated with blob and not for the whole namespace in the row.

similarly, GetBlobs() feels to me like it should be a message at shwap protocol level for fetching multiple blobs by commitment rather than just all -- right now.

You can request multiple blobs by commitment from different peers(as GetSamples) and make requests parallel. We can add this if it will be requested.
The point of give all blobs from this namespace is to support blob subscription.

And not to bikeshed, but similarly, would there be a need to fetch blobs from different namespaces but at the same height such that it would be more efficient to batch it in one request?

We are not requesting samples as batches from the security standpoint. Maybe we should follow this approach with blobs as well?

Can we DRY between Blob()/Blobs() methods between rsmt2d.go and ods.go

Yes

Why do we need two methods on accessor? Blob + Blobs? Can we not just have 1 (Blobs) and pass in 1 or multiple there? Right now, this is probably b/c we have the binary 1 blob or ALL blobs.

Right. Thank you.

feels like missing lots of unit tests

Already working on them.

Comment on lines 123 to 126
if err != nil {
if strings.Contains(err.Error(), blob.ErrBlobNotFound.Error()) {
if strings.Contains(err.Error(), shwap.ErrBlobNotFound.Error()) {
return nil, nil //nolint:nilnil
}
Copy link
Member

Choose a reason for hiding this comment

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

hmm, why don't we return non-inclusion proof?

Copy link
Member Author

@vgonkivs vgonkivs Feb 16, 2026

Choose a reason for hiding this comment

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

We havent returned it previously

Comment on lines +89 to +91
// GetBlobs retrieves all blobs belonging to the given namespace from the
// Extended Data Square (EDS) referenced by the given header.
GetBlobs(_ context.Context, _ *header.ExtendedHeader, _ libshare.Namespace) ([]*Blob, error)
Copy link
Member

Choose a reason for hiding this comment

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

Why you added this method? It seems to duplicate blobs parsing from blob service but leaking into reader level for some reason.

Comment on lines +42 to +46

// Blobs retrieves blobs belonging to the given namespace from the data square.
// When commitments are provided, only blobs matching those commitments are returned.
// When no commitments are provided, all blobs in the namespace are returned.
Blobs(context.Context, libshare.Namespace, ...[]byte) ([]*shwap.Blob, error)
Copy link
Member

Choose a reason for hiding this comment

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

It is a bit confusing why some APIs and interfaces are requesting single blobs while others are requesting multiple. We used to have ability to request only one blob by commitment. Are there some fundamentl changes required to make this PR? Imo ability to request multiple blobs by commitment at the same time is out of scope of this change and should be discussed / implemented separetly.

Copy link
Member Author

Choose a reason for hiding this comment

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

Top API level contains 2 independent methods(GetBlob/GetBlobs), while accessor has only one, since the logic is pretty the same and @renaynay suggested to have only one method.

Comment on lines +192 to +209
func (eds *Rsmt2D) extendedRowShares(ctx context.Context) ([][]libshare.Share, int, error) {
size, err := eds.Size(ctx)
if err != nil {
return nil, 0, err
}

odsSize := size / 2
shares := make([][]libshare.Share, odsSize)
for index := 0; index < odsSize; index++ {
rawShare := eds.Row(uint(index))
sh, err := libshare.FromBytes(rawShare)
if err != nil {
return nil, 0, err
}
shares[index] = sh
}
return shares, odsSize, nil
}
Copy link
Member

Choose a reason for hiding this comment

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

It is duplication. You can get extended row from just Row method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind:feat Attached to feature PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants