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: expose the data root inclusion proof over gRPC #1603

Merged
merged 3 commits into from
Feb 5, 2025
Merged
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
644 changes: 564 additions & 80 deletions proto/tendermint/rpc/grpc/types.pb.go

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions proto/tendermint/rpc/grpc/types.proto
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import "tendermint/abci/types.proto";
import "tendermint/types/types.proto";
import "tendermint/p2p/types.proto";
import "tendermint/crypto/keys.proto";
import "tendermint/crypto/proof.proto";
import "tendermint/types/validator.proto";
import "google/protobuf/timestamp.proto";
import "gogoproto/gogo.proto";
@@ -51,6 +52,15 @@ message SubscribeNewHeightsRequest {}

message StatusRequest {}

message DataRootInclusionProofRequest {
// Height the height of block we want to prove.
int64 height = 1;
// Start the start of the data commitment range containing the block.
uint64 start = 2;
// End the end exclusive of the data commitment range containing the block.
uint64 end = 3;
}

//----------------------------------------
// Response types

@@ -128,6 +138,10 @@ message ValidatorInfo {
int64 voting_power = 3;
}

message DataRootInclusionProofResponse {
tendermint.crypto.Proof proof = 1 [(gogoproto.nullable) = false];
}

//----------------------------------------
// Service Definition

@@ -144,3 +158,10 @@ service BlockAPI {
rpc SubscribeNewHeights(SubscribeNewHeightsRequest) returns (stream NewHeightEvent);
rpc Status(StatusRequest) returns (StatusResponse);
}

service BlobstreamAPI {
// DataRootInclusionProof creates an inclusion proof for the data root of block
// height `height` in the set of blocks defined by `start` and `end`. The range
// is end exclusive.
rpc DataRootInclusionProof(DataRootInclusionProofRequest) returns (DataRootInclusionProofResponse);
}
13 changes: 12 additions & 1 deletion rpc/core/blocks.go
Original file line number Diff line number Diff line change
@@ -235,6 +235,17 @@ func DataRootInclusionProof(
end uint64,
) (*ctypes.ResultDataRootInclusionProof, error) {
//nolint:gosec
proof, err := GenerateDataRootInclusionProof(height, start, end)
if err != nil {
return nil, err
}
return &ctypes.ResultDataRootInclusionProof{Proof: *proof}, nil
}

func GenerateDataRootInclusionProof(height int64, start, end uint64) (*merkle.Proof, error) {
if globalEnv == nil {
return nil, errors.New("global env is nil. this can only be called inside celestia-core")
}
err := validateDataRootInclusionProofRequest(uint64(height), start, end)
if err != nil {
return nil, err
@@ -247,7 +258,7 @@ func DataRootInclusionProof(
if err != nil {
return nil, err
}
return &ctypes.ResultDataRootInclusionProof{Proof: *proof}, nil
return proof, nil
}

// padBytes Pad bytes to given length
17 changes: 17 additions & 0 deletions rpc/grpc/api.go
Original file line number Diff line number Diff line change
@@ -416,3 +416,20 @@ func (blockAPI *BlockAPI) SubscribeNewHeights(_ *SubscribeNewHeightsRequest, str
}
}
}

type BlobstreamAPI struct{}

func NewBlobstreamAPI() *BlobstreamAPI {
return &BlobstreamAPI{}
}

func (blockAPI *BlobstreamAPI) DataRootInclusionProof(_ context.Context, req *DataRootInclusionProofRequest) (*DataRootInclusionProofResponse, error) {
proof, err := core.GenerateDataRootInclusionProof(req.Height, req.Start, req.End)
if err != nil {
return nil, err
}

return &DataRootInclusionProofResponse{
Proof: *proof.ToProto(),
}, nil
}
27 changes: 23 additions & 4 deletions rpc/grpc/client_server.go
Original file line number Diff line number Diff line change
@@ -23,13 +23,15 @@ type Config struct {
func StartGRPCServer(ln net.Listener) error {
grpcServer := grpc.NewServer()
RegisterBroadcastAPIServer(grpcServer, &broadcastAPI{})
api := NewBlockAPI()
RegisterBlockAPIServer(grpcServer, api)
blockAPI := NewBlockAPI()
RegisterBlockAPIServer(grpcServer, blockAPI)
blobstreamAPI := NewBlobstreamAPI()
RegisterBlobstreamAPIServer(grpcServer, blobstreamAPI)
errCh := make(chan error, 2)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
errCh <- api.StartNewBlockEventListener(ctx)
errCh <- blockAPI.StartNewBlockEventListener(ctx)
}()
go func() {
errCh <- grpcServer.Serve(ln)
@@ -40,7 +42,7 @@ func StartGRPCServer(ln net.Listener) error {
if err != nil {
core.GetEnvironment().Logger.Error("error stopping block api", "err", err)
}
}(api, ctx)
}(blockAPI, ctx)
// blocks until one errors or returns nil
return <-errCh
}
@@ -75,3 +77,20 @@ func StartBlockAPIGRPCClient(protoAddr string, opts ...grpc.DialOption) (BlockAP
func dialerFunc(ctx context.Context, addr string) (net.Conn, error) {
return cmtnet.Connect(addr)
}

// StartBlobstreamAPIGRPCClient dials the gRPC server using protoAddr and returns a new
// BlobstreamAPIClient.
func StartBlobstreamAPIGRPCClient(protoAddr string, opts ...grpc.DialOption) (BlobstreamAPIClient, error) {
if len(opts) == 0 {
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
}
opts = append(opts, grpc.WithContextDialer(dialerFunc))
conn, err := grpc.Dial( //nolint:staticcheck
protoAddr,
opts...,
)
if err != nil {
return nil, err
}
return NewBlobstreamAPIClient(conn), nil
}
20 changes: 20 additions & 0 deletions rpc/grpc/grpc_test.go
Original file line number Diff line number Diff line change
@@ -392,6 +392,26 @@ func TestBlockQuery_Streaming(t *testing.T) {
assert.NotEqual(t, part2.BlockPart.Proof, crypto.Proof{})
}

func TestBlobstreamAPI(t *testing.T) {
client, err := rpctest.GetBlobstreamAPIClient()
require.NoError(t, err)
waitForHeight(t, 10)

resp, err := client.DataRootInclusionProof(
context.Background(),
&core_grpc.DataRootInclusionProofRequest{
Height: 6,
Start: 1,
End: 10,
},
)
assert.NoError(t, err)
assert.NotNil(t, resp)
assert.Equal(t, int64(9), resp.Proof.Total)
assert.Equal(t, int64(5), resp.Proof.Index)
assert.Equal(t, 4, len(resp.Proof.Aunts))
}

func waitForHeight(t *testing.T, height int64) {
rpcAddr := rpctest.GetConfig().RPC.ListenAddress
c, err := rpchttp.New(rpcAddr, "/websocket")
644 changes: 564 additions & 80 deletions rpc/grpc/types.pb.go

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions rpc/test/helpers.go
Original file line number Diff line number Diff line change
@@ -126,6 +126,15 @@ func GetBlockAPIClient() (core_grpc.BlockAPIClient, error) {
return client, nil
}

func GetBlobstreamAPIClient() (core_grpc.BlobstreamAPIClient, error) {
grpcAddr := globalConfig.RPC.GRPCListenAddress
client, err := core_grpc.StartBlobstreamAPIGRPCClient(grpcAddr)
if err != nil {
return nil, err
}
return client, nil
}

// StartTendermint starts a test CometBFT server in a go routine and returns when it is initialized
func StartTendermint(app abci.Application, opts ...func(*Options)) *nm.Node {
nodeOpts := defaultOptions
Loading