diff --git a/go/consensus/api/light.go b/go/consensus/api/light.go index e1a4498d7ba..559ce62bc04 100644 --- a/go/consensus/api/light.go +++ b/go/consensus/api/light.go @@ -22,6 +22,10 @@ type LightService interface { // LightClient is a consensus light client interface. type LightClient interface { + // GetStoredLightBlock retrieves a light block from the local light block store without doing + // any remote queries. + GetStoredLightBlock(height int64) (*LightBlock, error) + // GetLightBlock queries peers for a specific light block. GetLightBlock(ctx context.Context, height int64) (*LightBlock, rpc.PeerFeedback, error) diff --git a/go/consensus/cometbft/cometbft.go b/go/consensus/cometbft/cometbft.go index 301707c322e..dcebff17f37 100644 --- a/go/consensus/cometbft/cometbft.go +++ b/go/consensus/cometbft/cometbft.go @@ -33,6 +33,12 @@ func New( } // NewLightClient creates a new CometBFT light client service. -func NewLightClient(ctx context.Context, dataDir string, genesis *genesisAPI.Document, consensus consensusAPI.Backend, p2p rpc.P2P) (lightAPI.ClientService, error) { +func NewLightClient( + ctx context.Context, + dataDir string, + genesis *genesisAPI.Document, + consensus consensusAPI.Backend, + p2p rpc.P2P, +) (lightAPI.ClientService, error) { return light.New(ctx, dataDir, genesis, consensus, p2p) } diff --git a/go/consensus/cometbft/full/full.go b/go/consensus/cometbft/full/full.go index 69efb59a3c3..ef3596053ff 100644 --- a/go/consensus/cometbft/full/full.go +++ b/go/consensus/cometbft/full/full.go @@ -47,7 +47,6 @@ import ( "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/db" lightAPI "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/light/api" "github.com/oasisprotocol/oasis-core/go/consensus/metrics" - lightP2P "github.com/oasisprotocol/oasis-core/go/consensus/p2p/light" genesisAPI "github.com/oasisprotocol/oasis-core/go/genesis/api" cmflags "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/flags" cmmetrics "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/metrics" @@ -473,9 +472,6 @@ func (t *fullService) RegisterP2PService(p2p p2pAPI.Service) error { } t.p2p = p2p - // Register consensus protocol server. - t.p2p.RegisterProtocolServer(lightP2P.NewServer(t.p2p, t.genesis.ChainContext(), t)) - return nil } diff --git a/go/consensus/cometbft/light/api/light.go b/go/consensus/cometbft/light/api/light.go index 024c4135915..08146c977a0 100644 --- a/go/consensus/cometbft/light/api/light.go +++ b/go/consensus/cometbft/light/api/light.go @@ -3,6 +3,7 @@ package api import ( "context" + "fmt" cmtlight "github.com/cometbft/cometbft/light" cmtlightprovider "github.com/cometbft/cometbft/light/provider" @@ -54,3 +55,20 @@ type ClientConfig struct { // TrustOptions are CometBFT light client trust options. TrustOptions cmtlight.TrustOptions } + +// NewLightBlock creates a new consensus.LightBlock from a CometBFT light block. +func NewLightBlock(clb *cmttypes.LightBlock) (*consensus.LightBlock, error) { + plb, err := clb.ToProto() + if err != nil { + return nil, fmt.Errorf("failed to marshal light block: %w", err) + } + meta, err := plb.Marshal() + if err != nil { + return nil, fmt.Errorf("failed to marshal light block: %w", err) + } + + return &consensus.LightBlock{ + Height: clb.Height, + Meta: meta, + }, nil +} diff --git a/go/consensus/cometbft/light/internal.go b/go/consensus/cometbft/light/internal.go index 68ab39c6654..7c6836c244d 100644 --- a/go/consensus/cometbft/light/internal.go +++ b/go/consensus/cometbft/light/internal.go @@ -61,6 +61,15 @@ func tryProviders[R any]( return result, nil, err } +// GetStoredBlock implements api.Client. +func (lc *lightClient) GetStoredLightBlock(height int64) (*consensus.LightBlock, error) { + clb, err := lc.tmc.TrustedLightBlock(height) + if err != nil { + return nil, err + } + return api.NewLightBlock(clb) +} + // GetLightBlock implements api.Client. func (lc *lightClient) GetLightBlock(ctx context.Context, height int64) (*consensus.LightBlock, rpc.PeerFeedback, error) { return tryProviders(ctx, lc, func(p api.Provider) (*consensus.LightBlock, rpc.PeerFeedback, error) { diff --git a/go/consensus/cometbft/light/p2p/p2p.go b/go/consensus/cometbft/light/p2p/p2p.go index 59d3895055d..d7eece7bfaa 100644 --- a/go/consensus/cometbft/light/p2p/p2p.go +++ b/go/consensus/cometbft/light/p2p/p2p.go @@ -245,6 +245,12 @@ func (lp *lightClientProvider) SubmitEvidence(ctx context.Context, evidence *con return pf, nil } +// GetStoredLightBlock implements api.Provider. +func (lp *lightClientProvider) GetStoredLightBlock(_ int64) (*consensus.LightBlock, error) { + // The remote client provider stores no blocks locally. + return nil, consensus.ErrVersionNotFound +} + // GetLightBlock implements api.Provider. func (lp *lightClientProvider) GetLightBlock(ctx context.Context, height int64) (*consensus.LightBlock, rpc.PeerFeedback, error) { peerID := lp.getPeer() diff --git a/go/consensus/cometbft/light/service.go b/go/consensus/cometbft/light/service.go index b3578b803f3..16b07a28407 100644 --- a/go/consensus/cometbft/light/service.go +++ b/go/consensus/cometbft/light/service.go @@ -258,6 +258,15 @@ func (c *client) worker() { } } +// GetStoredBlock implements api.Client. +func (c *client) GetStoredLightBlock(height int64) (*consensus.LightBlock, error) { + clb, err := c.store.LightBlock(height) + if err != nil { + return nil, err + } + return api.NewLightBlock(clb) +} + // GetLightBlock implements api.Client. func (c *client) GetLightBlock(ctx context.Context, height int64) (*consensus.LightBlock, rpc.PeerFeedback, error) { select { @@ -291,19 +300,11 @@ func (c *client) GetLightBlock(ctx context.Context, height int64) (*consensus.Li return nil, nil, err } - plb, err := clb.ToProto() + lb, err := api.NewLightBlock(clb) if err != nil { - return nil, nil, fmt.Errorf("failed to marshal light block: %w", err) - } - meta, err := plb.Marshal() - if err != nil { - return nil, nil, fmt.Errorf("failed to marshal light block: %w", err) + return nil, nil, err } - - return &consensus.LightBlock{ - Height: clb.Height, - Meta: meta, - }, rpc.NewNopPeerFeedback(), nil + return lb, rpc.NewNopPeerFeedback(), nil } // Direct peer query. diff --git a/go/consensus/p2p/light/server.go b/go/consensus/p2p/light/server.go index 0d1dc3cb62f..f0d907998c9 100644 --- a/go/consensus/p2p/light/server.go +++ b/go/consensus/p2p/light/server.go @@ -20,7 +20,9 @@ const ( type service struct { consensus consensus.Backend - logger *logging.Logger + lc consensus.LightClient + + logger *logging.Logger } func (s *service) HandleRequest(ctx context.Context, method string, body cbor.RawMessage) (interface{}, error) { @@ -30,12 +32,7 @@ func (s *service) HandleRequest(ctx context.Context, method string, body cbor.Ra if err := cbor.Unmarshal(body, &rq); err != nil { return nil, rpc.ErrBadRequest } - - lb, err := s.consensus.GetLightBlock(ctx, rq) - if err != nil { - return nil, err - } - return lb, nil + return s.handleGetLightBlock(ctx, rq) case MethodGetParameters: var rq int64 if err := cbor.Unmarshal(body, &rq); err != nil { @@ -59,11 +56,32 @@ func (s *service) HandleRequest(ctx context.Context, method string, body cbor.Ra } } +func (s *service) handleGetLightBlock(ctx context.Context, height int64) (*consensus.LightBlock, error) { + lb, err := s.consensus.GetLightBlock(ctx, height) + if err != nil { + // Also try the local light store. + if lb, err = s.lc.GetStoredLightBlock(height); err != nil { + return nil, err + } + } + return lb, nil +} + // NewServer creates a new light block sync protocol server. -func NewServer(p2p rpc.P2P, chainContext string, consensus consensus.Backend) rpc.Server { +func NewServer( + p2p rpc.P2P, + chainContext string, + consensus consensus.Backend, + lightClient consensus.LightClient, +) rpc.Server { p2p.RegisterProtocol(ProtocolID(chainContext), minProtocolPeers, totalProtocolPeers) + return rpc.NewServer( ProtocolID(chainContext), - &service{consensus, logging.GetLogger("consensus/p22/light/server")}, + &service{ + consensus: consensus, + lc: lightClient, + logger: logging.GetLogger("consensus/p2p/light/server"), + }, ) } diff --git a/go/oasis-node/cmd/node/node.go b/go/oasis-node/cmd/node/node.go index 146ecafbd72..328122c18e1 100644 --- a/go/oasis-node/cmd/node/node.go +++ b/go/oasis-node/cmd/node/node.go @@ -18,6 +18,7 @@ import ( "github.com/oasisprotocol/oasis-core/go/config" consensusAPI "github.com/oasisprotocol/oasis-core/go/consensus/api" "github.com/oasisprotocol/oasis-core/go/consensus/cometbft" + consensusLightP2P "github.com/oasisprotocol/oasis-core/go/consensus/p2p/light" controlAPI "github.com/oasisprotocol/oasis-core/go/control/api" genesisAPI "github.com/oasisprotocol/oasis-core/go/genesis/api" governanceAPI "github.com/oasisprotocol/oasis-core/go/governance/api" @@ -581,6 +582,9 @@ func NewNode() (node *Node, err error) { // nolint: gocyclo } node.svcMgr.Register(node.LightClient) + // Register consensus light client P2P protocol server. + node.P2P.RegisterProtocolServer(consensusLightP2P.NewServer(node.P2P, node.chainContext, node.Consensus, node.LightClient)) + // If the consensus backend supports communicating with consensus services, we can also start // all services required for runtime operation. if node.Consensus.SupportedFeatures().Has(consensusAPI.FeatureServices) {