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: support gRPC endpoints in core #1513

Open
wants to merge 44 commits into
base: v0.34.x-celestia
Choose a base branch
from
Open

Conversation

rach-id
Copy link
Member

@rach-id rach-id commented Oct 8, 2024

This is an implementation of a streaming API for blocks in core.

Helps close celestiaorg/celestia-app#3421 but not sure it entirely closes it.

It can easily be used:

package main

import (
	"context"
	"fmt"
	coregrpc "github.com/tendermint/tendermint/rpc/grpc"
)

func main() {
	client := coregrpc.StartBlockAPIGRPCClient("tcp://localhost:9090")

	blockStreamer, err := client.BlockByHeight(context.Background(), &coregrpc.BlockByHeightRequest{Height: 2})
	if err != nil {
		panic(err)
	}
	blockMeta, err := client.BlockMetaByHeight(context.Background(), &coregrpc.BlockMetaByHeightRequest{Height: 2})
	if err != nil {
		panic(err)
	}
	parts := make([]*core.Part, 0)
	for i := 0; i < int(blockMeta.BlockMeta.BlockID.PartSetHeader.Total); i++ {
		resp, err := blockStreamer.Recv()
		if err != nil {
			panic(err)
		}
		parts = append(parts, resp.BlockPart)
		if resp.IsLast && i < int(blockMeta.BlockMeta.BlockID.PartSetHeader.Total)-1 {
			panic("couldn't get all parts")
		} else if resp.IsLast {
			break
		}
	}

	h := types.NewPartSetFromHeader(types.PartSetHeader{
		Total: blockMeta.BlockMeta.BlockID.PartSetHeader.Total,
		Hash:  blockMeta.BlockMeta.BlockID.PartSetHeader.Hash,
	})

	for _, part := range parts {
		ok, err := h.AddPart(&types.Part{
			Index: part.Index,
			Bytes: part.Bytes,
			Proof: merkle.Proof{
				Total:    part.Proof.Total,
				Index:    part.Proof.Index,
				LeafHash: part.Proof.LeafHash,
				Aunts:    part.Proof.Aunts,
			},
		})
		if err != nil {
			panic(err)
		}
		if !ok {
			panic("not okey")
		}
	}
	pbb := new(core.Block)
	bz, err := io.ReadAll(h.GetReader())
	if err != nil {
		panic(err)
	}
	err = proto.Unmarshal(bz, pbb)
	if err != nil {
		panic(err)
	}
	block, err := types.BlockFromProto(pbb)
	if err != nil {
		panic(err)
	}
	fmt.Println(block)

	// get a commit
	commit, err := client.Commit(context.Background(), &coregrpc.CommitRequest{Height: 10})
	if err != nil {
		panic(err)
	}
	fmt.Println(commit)

	// listen for new heights
	streamer, err := client.SubscribeNewHeights(context.Background(), &coregrpc.SubscribeNewHeightsRequest{})
	if err != nil {
		panic(err)
	}
	for {
		resp, err := streamer.Recv()
		if err != nil {
			panic(err)
		}
		fmt.Println(resp)
	}
}

Ps: I didn't add the tests because I didn't find a direct way of mocking the environment without polluting the rest of the repo (exporting some methods, adding new helpers, etc). And I think since the implementation is simple, just querying the block/state stores for results, it's fine to leave it untested.

@rach-id rach-id self-assigned this Oct 8, 2024
@rach-id rach-id requested a review from a team as a code owner October 8, 2024 09:44
@rach-id rach-id requested review from staheri14 and ninabarbakadze and removed request for a team October 8, 2024 09:44
@rach-id rach-id marked this pull request as draft October 8, 2024 11:40
@rach-id
Copy link
Member Author

rach-id commented Oct 8, 2024

Just found out that once we enable the app grpc, these endpoints get overridden, I'll be looking into it

proto/tendermint/rpc/grpc/types.proto Outdated Show resolved Hide resolved
proto/tendermint/rpc/grpc/types.proto Outdated Show resolved Hide resolved
rpc/grpc/api.go Outdated Show resolved Hide resolved
rpc/grpc/api.go Outdated Show resolved Hide resolved
rpc/grpc/api.go Outdated Show resolved Hide resolved
evan-forbes
evan-forbes previously approved these changes Nov 8, 2024
@rach-id
Copy link
Member Author

rach-id commented Nov 8, 2024

This branch is now ready to be merged:

  • Tests added
  • TLS support added to utility as per @vgonkivs request
  • latest version tested in node against an app running this version and all seems to be in place

evan-forbes
evan-forbes previously approved these changes Nov 12, 2024
@rootulp rootulp self-requested a review November 18, 2024 15:34
Copy link
Contributor

@cmwaters cmwaters left a comment

Choose a reason for hiding this comment

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

Thanks for E2E testing this change @rach-id. Have a few questions before we merge

Comment on lines 62 to 76
message BlockByHashResponse {
tendermint.types.Part block_part = 1;
tendermint.types.Commit commit = 2;
tendermint.types.ValidatorSet validator_set = 3;
tendermint.types.BlockMeta block_meta = 4;
bool is_last = 5;
}

message BlockByHeightResponse {
tendermint.types.Part block_part = 1;
tendermint.types.Commit commit = 2;
tendermint.types.ValidatorSet validator_set = 3;
tendermint.types.BlockMeta block_meta = 4;
bool is_last = 5;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible to prefix these types as StreamedBlockByHashResponse for example so it's more obvious that this type is streamed. Also could we add a comment that the commit, validator set and block meta is only populated for the first response in that stream? (I guess this is how it works right).

Also why BlockMeta and not just Header?

Copy link
Member Author

Choose a reason for hiding this comment

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

Is it possible to prefix these types as StreamedBlockByHashResponse for example so it's more obvious that this type is streamed. Also could we add a comment that the commit, validator set and block meta is only populated for the first response in that stream? (I guess this is how it works right).

ff4f343

Also why BlockMeta and not just Header?

that's what node team asked for

return ps.AddPartWithoutProof(part)
}

func (ps *PartSet) AddPartWithoutProof(part *Part) (bool, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see where else we call this. What was the reason for splitting?

Copy link
Member Author

Choose a reason for hiding this comment

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

it's called in Celestia-node to have the parts and build the block without verifying the proof

Copy link
Contributor

Choose a reason for hiding this comment

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

why do we want to build the block without verifying the proof

Copy link
Member Author

Choose a reason for hiding this comment

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

Since they make the assumption that every bridge node will be connected to a local full node. So the data is trusted and no need to verify inclusion. Also to reduce the size of the parts.

The initial implementation provided the proofs by default, but they said its better to disable it by default

evan-forbes
evan-forbes previously approved these changes Nov 19, 2024
rootulp
rootulp previously approved these changes Nov 19, 2024
Copy link
Collaborator

@rootulp rootulp left a comment

Choose a reason for hiding this comment

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

LGTM. Left a few non-blocking questions / suggestions

rpc/grpc/api.go Outdated Show resolved Hide resolved
Comment on lines +116 to +117
env.Logger.Debug("couldn't cast event data to new block")
continue
Copy link
Collaborator

Choose a reason for hiding this comment

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

[question] should this log an error and terminate instead of continuing?

Suggested change
env.Logger.Debug("couldn't cast event data to new block")
continue
env.Logger.Error("couldn't cast event data to new block")
return

Copy link
Member Author

Choose a reason for hiding this comment

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

I didn't want to have the gRPC logs polluting the output but it's a good point. I will handle this in a separate PR where I will be doing more testing and adding more logic for failures etc. I didn't want to include everything in the same PR to reduce complexity

defer ticker.Stop()
blockAPI.Lock()
defer blockAPI.Unlock()
for i := 1; i < 6; i++ {
Copy link
Collaborator

@rootulp rootulp Nov 19, 2024

Choose a reason for hiding this comment

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

[question] Should we extract a constant for RETRY_ATTEMPTS = 5

Suggested change
for i := 1; i < 6; i++ {
for i := 0; i < RETRY_ATTEMPTS; i++ {

Copy link
Member Author

Choose a reason for hiding this comment

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

yes, we could. I can extract it if you want

Co-authored-by: Rootul P <[email protected]>
@rach-id rach-id dismissed stale reviews from rootulp and evan-forbes via cee4b95 November 19, 2024 16:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Expose all celestia-node required endpoints through gRPC
6 participants