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/api blob proofs #312

Merged
merged 13 commits into from
Dec 5, 2024
77 changes: 77 additions & 0 deletions cmd/api/handler/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import (
"context"
"encoding/base64"
"encoding/hex"
"github.com/celestiaorg/celestia-app/v3/pkg/appconsts"
"github.com/celestiaorg/celestia-app/v3/pkg/da"
"github.com/celestiaorg/go-square/shares"
"net/http"
"time"

Expand All @@ -17,6 +20,7 @@ import (
"github.com/celenium-io/celestia-indexer/internal/storage"
testsuite "github.com/celenium-io/celestia-indexer/internal/test_suite"
"github.com/celenium-io/celestia-indexer/pkg/node"
"github.com/celestiaorg/go-square/square"
"github.com/labstack/echo/v4"
)

Expand All @@ -27,6 +31,7 @@ type NamespaceHandler struct {
address storage.IAddress
blob node.DalApi
state storage.IState
node node.Api
indexerName string
}

Expand All @@ -38,6 +43,7 @@ func NewNamespaceHandler(
state storage.IState,
indexerName string,
blob node.DalApi,
node node.Api,
) *NamespaceHandler {
return &NamespaceHandler{
namespace: namespace,
Expand All @@ -47,6 +53,7 @@ func NewNamespaceHandler(
blob: blob,
state: state,
indexerName: indexerName,
node: node,
}
}

Expand Down Expand Up @@ -733,3 +740,73 @@ func (handler *NamespaceHandler) Rollups(c echo.Context) error {

return returnArray(c, response)
}

// BlobProofs godoc
//
// @Summary Get blob inclusion proofs
// @Description Returns blob inclusion proofs
// @Tags namespace
// @ID get-blob-proof
// @Param request body postBlobRequest true "Request body containing height, commitment and namespace hash"
// @Accept json
// @Produce json
// @Success 200 {object} responses.BlobLog
// @Failure 400 {object} Error
// @Router /blob/proofs [get]
func (handler *NamespaceHandler) BlobProofs(c echo.Context) error {
req, err := bindAndValidate[postBlobRequest](c)
if err != nil {
return badRequestError(c, err)
}

block, err := handler.node.Block(c.Request().Context(), req.Height)
if err != nil {
return handleError(c, err, handler.namespace)
}

dataSquare, err := square.Construct(
block.Block.Data.Txs.ToSliceOfBytes(),
appconsts.SquareSizeUpperBound(0),
appconsts.SubtreeRootThreshold(0),
)

if err != nil {
return internalServerError(c, err)
}

eds, err := da.ExtendShares(shares.ToBytes(dataSquare))
if err != nil {
return internalServerError(c, err)
}

ods, err := responses.NewODS(eds)
if err != nil {
return internalServerError(c, err)
}

namespaceOds, err := ods.FindODSByNamespace(req.Hash)
if err != nil {
return internalServerError(c, err)
}

namespaceShares, err := responses.GetNamespaceShares(eds, namespaceOds.From, namespaceOds.To)
if err != nil {
return internalServerError(c, err)
}

startBlobIdx, endBlobIdx, err := responses.GetBlobShareIdxs(
namespaceShares,
namespaceOds.From,
eds.Width()/2,
req.Commitment,
)
if err != nil {
return internalServerError(c, err)
}

proofs, err := handler.node.BlobProofs(c.Request().Context(), req.Height, startBlobIdx, endBlobIdx)
Copy link
Member

Choose a reason for hiding this comment

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

Здесь может быть startBlobIdx == endBlobIdx == 0. По крайней мере у меня так получилось с первым попавшимся блобом. Из-=за этого нода пятистоит

if err != nil {
return handleError(c, err, handler.namespace)
}
return c.JSON(http.StatusOK, proofs)
}
12 changes: 11 additions & 1 deletion cmd/api/handler/namespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type NamespaceTestSuite struct {
address *mock.MockIAddress
state *mock.MockIState
blobReceiver *nodeMock.MockDalApi
node *nodeMock.MockApi
echo *echo.Echo
handler *NamespaceHandler
ctrl *gomock.Controller
Expand All @@ -67,7 +68,16 @@ func (s *NamespaceTestSuite) SetupSuite() {
s.rollups = mock.NewMockIRollup(s.ctrl)
s.state = mock.NewMockIState(s.ctrl)
s.blobReceiver = nodeMock.NewMockDalApi(s.ctrl)
s.handler = NewNamespaceHandler(s.namespaces, s.blobLogs, s.rollups, s.address, s.state, testIndexerName, s.blobReceiver)
s.handler = NewNamespaceHandler(
s.namespaces,
s.blobLogs,
s.rollups,
s.address,
s.state,
testIndexerName,
s.blobReceiver,
s.node,
)
}

// TearDownSuite -
Expand Down
121 changes: 120 additions & 1 deletion cmd/api/handler/responses/ods.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@ package responses

import (
"encoding/base64"

"github.com/celestiaorg/celestia-app/v3/pkg/appconsts"
"github.com/celestiaorg/go-square/namespace"
"github.com/celestiaorg/go-square/shares"
incl "github.com/celestiaorg/go-square/v2/inclusion"
"github.com/celestiaorg/go-square/v2/share"
"github.com/pkg/errors"
"github.com/tendermint/tendermint/crypto/merkle"

_ "github.com/celestiaorg/go-square/v2/share"
"github.com/celestiaorg/rsmt2d"
)

Expand All @@ -24,6 +30,24 @@ type ODSItem struct {
Type NamespaceKind `json:"type"`
}

type sequence struct {
ns share.Namespace
shareVersion uint8
startShareIndex int
data []byte
sequenceLen uint32
signer []byte
}

func (ods *ODS) FindODSByNamespace(namespace string) (*ODSItem, error) {
for _, item := range ods.Items {
if item.Namespace == namespace {
return &item, nil
}
}
return nil, errors.New("item with specified namespace not found")
}

func NewODS(eds *rsmt2d.ExtendedDataSquare) (ODS, error) {
ods := ODS{
Width: eds.Width() / 2,
Expand Down Expand Up @@ -61,6 +85,40 @@ func NewODS(eds *rsmt2d.ExtendedDataSquare) (ODS, error) {
return ods, nil
}

func GetNamespaceShares(eds *rsmt2d.ExtendedDataSquare, from, to []uint) ([]share.Share, error) {
if len(from) != len(to) {
return nil, errors.New("length of 'from' and 'to' must match")
}

var resultShares []share.Share
startRow, startCol := from[0], from[1]
endRow, endCol := to[0], to[1]

if startRow > endRow || (startRow == endRow && startCol > endCol) {
return nil, errors.New("invalid from and to params")
}
currentRow, currentCol := startRow, startCol

for {
cell := eds.GetCell(currentRow, currentCol)
cellShare, err := share.NewShare(cell)
if err != nil {
return nil, err
}
resultShares = append(resultShares, *cellShare)
if currentRow == endRow && currentCol == endCol {
break
}
currentCol++
if currentCol == eds.Width()/2 {
currentCol = 0
currentRow++
}
}

return resultShares, nil
}

type NamespaceKind string

const (
Expand Down Expand Up @@ -88,3 +146,64 @@ func getNamespaceType(ns namespace.Namespace) NamespaceKind {
return DefaultNamespace
}
}

func GetBlobShareIdxs(
shares []share.Share,
nsStartFromIdx []uint,
edsWidth uint,
b64commitment string,
) (blobStartIdx, blobEndIdx int, err error) {
if len(shares) == 0 {
return 0, 0, errors.New("invalid shares length")

}
sequences := make([]sequence, 0)
startRow, startCol := nsStartFromIdx[0], nsStartFromIdx[1]
nsStartIdx := int(startRow*edsWidth + startCol)

for shareIdx, s := range shares {
if !(s.Version() <= 1) {
return 0, 0, errors.New("unsupported share version")
}

if s.IsPadding() {
continue
}

if s.IsSequenceStart() {
sequences = append(sequences, sequence{
ns: s.Namespace(),
shareVersion: s.Version(),
startShareIndex: shareIdx,
data: s.RawData(), // todo: remove all fields except this
sequenceLen: s.SequenceLen(),
signer: share.GetSigner(s),
})
} else {
if len(sequences) == 0 {
return 0, 0, errors.New("continuation share without a sequence start share")
}
aopoltorzhicky marked this conversation as resolved.
Show resolved Hide resolved
prev := &sequences[len(sequences)-1]
prev.data = append(prev.data, s.RawData()...)
}
}
for i, seq := range sequences {
seq.data = seq.data[:seq.sequenceLen]
blob, err := share.NewBlob(seq.ns, seq.data, seq.shareVersion, seq.signer)
if err != nil {
return 0, 0, err
}
commitment, err := incl.CreateCommitment(blob, merkle.HashFromByteSlices, appconsts.SubtreeRootThreshold(0))
if err != nil {
return 0, 0, err
}
if base64.StdEncoding.EncodeToString(commitment) == b64commitment {
if i == (len(sequences))-1 { // last sequence element
aopoltorzhicky marked this conversation as resolved.
Show resolved Hide resolved
return seq.startShareIndex, seq.startShareIndex, err
}

return nsStartIdx + seq.startShareIndex, nsStartIdx + sequences[i+1].startShareIndex - 1, err
}
}
return 0, 0, err
}
12 changes: 11 additions & 1 deletion cmd/api/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,13 +354,23 @@ func initHandlers(ctx context.Context, e *echo.Echo, cfg Config, db postgres.Sto
panic(err)
}

namespaceHandlers := handler.NewNamespaceHandler(db.Namespace, db.BlobLogs, db.Rollup, db.Address, db.State, cfg.Indexer.Name, blobReceiver)
namespaceHandlers := handler.NewNamespaceHandler(
db.Namespace,
db.BlobLogs,
db.Rollup,
db.Address,
db.State,
cfg.Indexer.Name,
blobReceiver,
&node,
)

blobGroup := v1.Group("/blob")
{
blobGroup.GET("", namespaceHandlers.Blobs)
blobGroup.POST("", namespaceHandlers.Blob)
blobGroup.POST("/metadata", namespaceHandlers.BlobMetadata)
blobGroup.POST("/proofs", namespaceHandlers.BlobProofs)
}

namespaceGroup := v1.Group("/namespace")
Expand Down
1 change: 1 addition & 0 deletions cmd/api/routes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ func TestRoutes(t *testing.T) {
"/v1/stats/messages_count_24h GET": {},
"/v1/rollup/stats/series GET": {},
"/v1/rollup/group GET": {},
"/v1/blob/proofs POST": {},
}

ctx, cancel := context.WithCancel(context.Background())
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
github.com/aws/smithy-go v1.20.2
github.com/celestiaorg/celestia-app/v3 v3.0.0-mocha
github.com/celestiaorg/go-square v1.1.1
github.com/celestiaorg/go-square/v2 v2.0.0
github.com/celestiaorg/go-square/v2 v2.1.0
github.com/celestiaorg/rsmt2d v0.14.0
github.com/cespare/xxhash v1.1.0
github.com/cosmos/cosmos-sdk v0.46.16
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ github.com/celestiaorg/go-square v1.1.1 h1:Cy3p8WVspVcyOqHM8BWFuuYPwMitO1pYGe+Im
github.com/celestiaorg/go-square v1.1.1/go.mod h1:1EXMErhDrWJM8B8V9hN7dqJ2kUTClfwdqMOmF9yQUa0=
github.com/celestiaorg/go-square/v2 v2.0.0 h1:U5QV8/de5lc7glosfgyHhcxbFwNuwU4+6aYZ2RgjM04=
github.com/celestiaorg/go-square/v2 v2.0.0/go.mod h1:y0BolG0tRM7UN1sAQyDDUkT+aMJPwFIjviVvnCB62C0=
github.com/celestiaorg/go-square/v2 v2.1.0 h1:ECIvYEeHIWiIJGDCJxQNtzqm5DmnBly7XGhSpLsl+Lw=
github.com/celestiaorg/go-square/v2 v2.1.0/go.mod h1:n3ztrh8CBjWOD6iWYMo3pPOlQIgzLK9yrnqMPcNo6g8=
github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4 h1:CJdIpo8n5MFP2MwK0gSRcOVlDlFdQJO1p+FqdxYzmvc=
github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4/go.mod h1:fzuHnhzj1pUygGz+1ZkB3uQbEUL4htqCGJ4Qs2LwMZA=
github.com/celestiaorg/nmt v0.22.2 h1:JmOMtZL9zWAed1hiwb9DDs+ELcKp/ZQZ3rPverge/V8=
Expand Down
1 change: 1 addition & 0 deletions pkg/node/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Api interface {
Genesis(ctx context.Context) (types.Genesis, error)
BlockData(ctx context.Context, level pkgTypes.Level) (pkgTypes.BlockData, error)
BlockDataGet(ctx context.Context, level pkgTypes.Level) (pkgTypes.BlockData, error)
BlobProofs(ctx context.Context, level pkgTypes.Level, startShare, endShare int) (pkgTypes.BlobProof, error)
Copy link
Member

Choose a reason for hiding this comment

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

По идее, это можно выпилить

}

//go:generate mockgen -source=$GOFILE -destination=mock/$GOFILE -package=mock -typed
Expand Down
Loading
Loading