diff --git a/cmd/lncli/chainrpc_active.go b/cmd/lncli/chainrpc_active.go index 0d8b891acb..48946e0d5d 100644 --- a/cmd/lncli/chainrpc_active.go +++ b/cmd/lncli/chainrpc_active.go @@ -25,6 +25,7 @@ func chainCommands() []cli.Command { getBlockCommand, getBestBlockCommand, getBlockHashCommand, + getBlockHeaderCommand, }, }, } @@ -113,6 +114,45 @@ func getBlock(ctx *cli.Context) error { return nil } +var getBlockHeaderCommand = cli.Command{ + Name: "getblockheader", + Usage: "Get a block header.", + Category: "On-chain", + Description: "Returns a block header with a particular block hash.", + ArgsUsage: "hash", + Action: actionDecorator(getBlockHeader), +} + +func getBlockHeader(ctx *cli.Context) error { + ctxc := getContext() + args := ctx.Args() + + // Display the command's help message if we do not have the expected + // number of arguments/flags. + if !args.Present() { + return cli.ShowCommandHelp(ctx, "getblockheader") + } + + blockHash, err := chainhash.NewHashFromStr(args.First()) + if err != nil { + return err + } + + req := &chainrpc.GetBlockHeaderRequest{BlockHash: blockHash[:]} + + client, cleanUp := getChainClient(ctx) + defer cleanUp() + + resp, err := client.GetBlockHeader(ctxc, req) + if err != nil { + return err + } + + printRespJSON(resp) + + return nil +} + var getBestBlockCommand = cli.Command{ Name: "getbestblock", Category: "On-chain", diff --git a/cmd/lncli/neutrino_active.go b/cmd/lncli/neutrino_active.go index 4267738e12..099da46c6e 100644 --- a/cmd/lncli/neutrino_active.go +++ b/cmd/lncli/neutrino_active.go @@ -155,16 +155,16 @@ func isBanned(ctx *cli.Context) error { return nil } -var getBlockHeaderCommand = cli.Command{ +var getBlockHeaderNeutrinoCommand = cli.Command{ Name: "getblockheader", Usage: "Get a block header.", Category: "Neutrino", Description: "Returns a block header with a particular block hash.", ArgsUsage: "hash", - Action: actionDecorator(getBlockHeader), + Action: actionDecorator(getBlockHeaderNeutrino), } -func getBlockHeader(ctx *cli.Context) error { +func getBlockHeaderNeutrino(ctx *cli.Context) error { ctxc := getContext() args := ctx.Args() @@ -239,7 +239,7 @@ func neutrinoCommands() []cli.Command { addPeerCommand, disconnectPeerCommand, isBannedCommand, - getBlockHeaderCommand, + getBlockHeaderNeutrinoCommand, getCFilterCommand, }, }, diff --git a/contractcourt/channel_arbitrator_test.go b/contractcourt/channel_arbitrator_test.go index 1fce269fd9..34c6122fda 100644 --- a/contractcourt/channel_arbitrator_test.go +++ b/contractcourt/channel_arbitrator_test.go @@ -187,6 +187,10 @@ func (*mockChainIO) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) return nil, nil } +func (*mockChainIO) GetBlockHeader(*chainhash.Hash) (*wire.BlockHeader, error) { + return nil, nil +} + type chanArbTestCtx struct { t *testing.T diff --git a/docs/release-notes/release-notes-0.17.1.md b/docs/release-notes/release-notes-0.17.1.md index a0e414f05c..2caf937c6b 100644 --- a/docs/release-notes/release-notes-0.17.1.md +++ b/docs/release-notes/release-notes-0.17.1.md @@ -36,6 +36,10 @@ instead. ## RPC Additions + +* [`chainrpc` `GetBlockHeader`](https://github.com/lightningnetwork/lnd/pull/8111) + can be used to get block headers with any chain backend. + ## lncli Additions # Improvements diff --git a/itest/lnd_onchain_test.go b/itest/lnd_onchain_test.go index 4cfe47d376..7bd97c8bd6 100644 --- a/itest/lnd_onchain_test.go +++ b/itest/lnd_onchain_test.go @@ -26,6 +26,7 @@ func testChainKit(ht *lntest.HarnessTest) { // during execution. By calling sub-test functions as seen below we // avoid the need to start separate nodes. testChainKitGetBlock(ht) + testChainKitGetBlockHeader(ht) testChainKitGetBlockHash(ht) testChainKitSendOutputsAnchorReserve(ht) } @@ -58,6 +59,49 @@ func testChainKitGetBlock(ht *lntest.HarnessTest) { require.Equal(ht, expected, actual) } +// testChainKitGetBlockHeader ensures that given a block hash, the RPC endpoint +// returns the correct target block header. +func testChainKitGetBlockHeader(ht *lntest.HarnessTest) { + // Get best block hash. + bestBlockRes := ht.Alice.RPC.GetBestBlock(nil) + + var ( + bestBlockHash chainhash.Hash + bestBlockHeader wire.BlockHeader + msgBlock = &wire.MsgBlock{} + ) + err := bestBlockHash.SetBytes(bestBlockRes.BlockHash) + require.NoError(ht, err) + + // Retrieve the best block by hash. + getBlockReq := &chainrpc.GetBlockRequest{ + BlockHash: bestBlockHash[:], + } + getBlockRes := ht.Alice.RPC.GetBlock(getBlockReq) + + // Deserialize the block which was retrieved by hash. + blockReader := bytes.NewReader(getBlockRes.RawBlock) + err = msgBlock.Deserialize(blockReader) + require.NoError(ht, err) + + // Retrieve the block header for the best block. + getBlockHeaderReq := &chainrpc.GetBlockHeaderRequest{ + BlockHash: bestBlockHash[:], + } + getBlockHeaderRes := ht.Alice.RPC.GetBlockHeader(getBlockHeaderReq) + + // Deserialize the block header which was retrieved by hash. + blockHeaderReader := bytes.NewReader(getBlockHeaderRes.RawBlockHeader) + err = bestBlockHeader.Deserialize(blockHeaderReader) + require.NoError(ht, err) + + // Ensure the header of the best block is the same as retrieved block + // header. + expected := bestBlockHeader + actual := msgBlock.Header + require.Equal(ht, expected, actual) +} + // testChainKitGetBlockHash ensures that given a block height, the RPC endpoint // returns the correct target block hash. func testChainKitGetBlockHash(ht *lntest.HarnessTest) { diff --git a/lnrpc/chainrpc/chain_server.go b/lnrpc/chainrpc/chain_server.go index 89f480b8b4..00a30c07c7 100644 --- a/lnrpc/chainrpc/chain_server.go +++ b/lnrpc/chainrpc/chain_server.go @@ -46,6 +46,10 @@ var ( Entity: "onchain", Action: "read", }}, + "/chainrpc.ChainKit/GetBlockHeader": {{ + Entity: "onchain", + Action: "read", + }}, "/chainrpc.ChainKit/GetBestBlock": {{ Entity: "onchain", Action: "read", @@ -293,6 +297,31 @@ func (s *Server) GetBlock(_ context.Context, return &GetBlockResponse{RawBlock: rawBlock}, nil } +// GetBlockHeader returns a block header given the corresponding block hash. +func (s *Server) GetBlockHeader(_ context.Context, + in *GetBlockHeaderRequest) (*GetBlockHeaderResponse, error) { + + // We'll start by reconstructing the RPC request into what the + // underlying chain functionality expects. + var blockHash chainhash.Hash + copy(blockHash[:], in.BlockHash) + + blockHeader, err := s.cfg.Chain.GetBlockHeader(&blockHash) + if err != nil { + return nil, err + } + + // Serialize block header for RPC response. + var headerBuf bytes.Buffer + err = blockHeader.Serialize(&headerBuf) + if err != nil { + return nil, err + } + rawHeader := headerBuf.Bytes() + + return &GetBlockHeaderResponse{RawBlockHeader: rawHeader}, nil +} + // GetBestBlock returns the latest block hash and current height of the valid // most-work chain. func (s *Server) GetBestBlock(_ context.Context, diff --git a/lnrpc/chainrpc/chainkit.pb.go b/lnrpc/chainrpc/chainkit.pb.go index 5dd261fd52..dc478f71fe 100644 --- a/lnrpc/chainrpc/chainkit.pb.go +++ b/lnrpc/chainrpc/chainkit.pb.go @@ -118,6 +118,102 @@ func (x *GetBlockResponse) GetRawBlock() []byte { return nil } +type GetBlockHeaderRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The hash of the block with the requested header. + BlockHash []byte `protobuf:"bytes,1,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"` +} + +func (x *GetBlockHeaderRequest) Reset() { + *x = GetBlockHeaderRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_chainrpc_chainkit_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlockHeaderRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlockHeaderRequest) ProtoMessage() {} + +func (x *GetBlockHeaderRequest) ProtoReflect() protoreflect.Message { + mi := &file_chainrpc_chainkit_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBlockHeaderRequest.ProtoReflect.Descriptor instead. +func (*GetBlockHeaderRequest) Descriptor() ([]byte, []int) { + return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{2} +} + +func (x *GetBlockHeaderRequest) GetBlockHash() []byte { + if x != nil { + return x.BlockHash + } + return nil +} + +type GetBlockHeaderResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The header of the block with the requested hash. + RawBlockHeader []byte `protobuf:"bytes,1,opt,name=raw_block_header,json=rawBlockHeader,proto3" json:"raw_block_header,omitempty"` +} + +func (x *GetBlockHeaderResponse) Reset() { + *x = GetBlockHeaderResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_chainrpc_chainkit_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlockHeaderResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlockHeaderResponse) ProtoMessage() {} + +func (x *GetBlockHeaderResponse) ProtoReflect() protoreflect.Message { + mi := &file_chainrpc_chainkit_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBlockHeaderResponse.ProtoReflect.Descriptor instead. +func (*GetBlockHeaderResponse) Descriptor() ([]byte, []int) { + return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{3} +} + +func (x *GetBlockHeaderResponse) GetRawBlockHeader() []byte { + if x != nil { + return x.RawBlockHeader + } + return nil +} + type GetBestBlockRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -127,7 +223,7 @@ type GetBestBlockRequest struct { func (x *GetBestBlockRequest) Reset() { *x = GetBestBlockRequest{} if protoimpl.UnsafeEnabled { - mi := &file_chainrpc_chainkit_proto_msgTypes[2] + mi := &file_chainrpc_chainkit_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -140,7 +236,7 @@ func (x *GetBestBlockRequest) String() string { func (*GetBestBlockRequest) ProtoMessage() {} func (x *GetBestBlockRequest) ProtoReflect() protoreflect.Message { - mi := &file_chainrpc_chainkit_proto_msgTypes[2] + mi := &file_chainrpc_chainkit_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -153,7 +249,7 @@ func (x *GetBestBlockRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetBestBlockRequest.ProtoReflect.Descriptor instead. func (*GetBestBlockRequest) Descriptor() ([]byte, []int) { - return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{2} + return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{4} } type GetBestBlockResponse struct { @@ -170,7 +266,7 @@ type GetBestBlockResponse struct { func (x *GetBestBlockResponse) Reset() { *x = GetBestBlockResponse{} if protoimpl.UnsafeEnabled { - mi := &file_chainrpc_chainkit_proto_msgTypes[3] + mi := &file_chainrpc_chainkit_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -183,7 +279,7 @@ func (x *GetBestBlockResponse) String() string { func (*GetBestBlockResponse) ProtoMessage() {} func (x *GetBestBlockResponse) ProtoReflect() protoreflect.Message { - mi := &file_chainrpc_chainkit_proto_msgTypes[3] + mi := &file_chainrpc_chainkit_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -196,7 +292,7 @@ func (x *GetBestBlockResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetBestBlockResponse.ProtoReflect.Descriptor instead. func (*GetBestBlockResponse) Descriptor() ([]byte, []int) { - return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{3} + return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{5} } func (x *GetBestBlockResponse) GetBlockHash() []byte { @@ -225,7 +321,7 @@ type GetBlockHashRequest struct { func (x *GetBlockHashRequest) Reset() { *x = GetBlockHashRequest{} if protoimpl.UnsafeEnabled { - mi := &file_chainrpc_chainkit_proto_msgTypes[4] + mi := &file_chainrpc_chainkit_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -238,7 +334,7 @@ func (x *GetBlockHashRequest) String() string { func (*GetBlockHashRequest) ProtoMessage() {} func (x *GetBlockHashRequest) ProtoReflect() protoreflect.Message { - mi := &file_chainrpc_chainkit_proto_msgTypes[4] + mi := &file_chainrpc_chainkit_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -251,7 +347,7 @@ func (x *GetBlockHashRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetBlockHashRequest.ProtoReflect.Descriptor instead. func (*GetBlockHashRequest) Descriptor() ([]byte, []int) { - return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{4} + return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{6} } func (x *GetBlockHashRequest) GetBlockHeight() int64 { @@ -273,7 +369,7 @@ type GetBlockHashResponse struct { func (x *GetBlockHashResponse) Reset() { *x = GetBlockHashResponse{} if protoimpl.UnsafeEnabled { - mi := &file_chainrpc_chainkit_proto_msgTypes[5] + mi := &file_chainrpc_chainkit_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -286,7 +382,7 @@ func (x *GetBlockHashResponse) String() string { func (*GetBlockHashResponse) ProtoMessage() {} func (x *GetBlockHashResponse) ProtoReflect() protoreflect.Message { - mi := &file_chainrpc_chainkit_proto_msgTypes[5] + mi := &file_chainrpc_chainkit_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -299,7 +395,7 @@ func (x *GetBlockHashResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetBlockHashResponse.ProtoReflect.Descriptor instead. func (*GetBlockHashResponse) Descriptor() ([]byte, []int) { - return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{5} + return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{7} } func (x *GetBlockHashResponse) GetBlockHash() []byte { @@ -320,39 +416,53 @@ var file_chainrpc_chainkit_proto_rawDesc = []byte{ 0x6b, 0x48, 0x61, 0x73, 0x68, 0x22, 0x2f, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x61, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, 0x61, - 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x15, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x42, 0x65, 0x73, - 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x58, 0x0a, - 0x14, 0x47, 0x65, 0x74, 0x42, 0x65, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, + 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x36, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x22, 0x42, + 0x0a, 0x16, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x61, 0x77, 0x5f, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0e, 0x72, 0x61, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x22, 0x15, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x42, 0x65, 0x73, 0x74, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x58, 0x0a, 0x14, 0x47, 0x65, 0x74, + 0x42, 0x65, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, + 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x22, 0x38, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, + 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x35, 0x0a, + 0x14, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x38, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, - 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x22, 0x35, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, - 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x32, 0xeb, 0x01, 0x0a, 0x08, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x4b, 0x69, 0x74, 0x12, 0x41, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x12, 0x19, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x42, - 0x65, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1d, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x48, 0x61, 0x73, 0x68, 0x32, 0xc0, 0x02, 0x0a, 0x08, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x69, + 0x74, 0x12, 0x41, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, 0x2e, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0c, 0x47, 0x65, 0x74, + 0x42, 0x65, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1d, 0x2e, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x65, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x65, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1d, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x65, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1d, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -367,24 +477,28 @@ func file_chainrpc_chainkit_proto_rawDescGZIP() []byte { return file_chainrpc_chainkit_proto_rawDescData } -var file_chainrpc_chainkit_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_chainrpc_chainkit_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_chainrpc_chainkit_proto_goTypes = []interface{}{ - (*GetBlockRequest)(nil), // 0: chainrpc.GetBlockRequest - (*GetBlockResponse)(nil), // 1: chainrpc.GetBlockResponse - (*GetBestBlockRequest)(nil), // 2: chainrpc.GetBestBlockRequest - (*GetBestBlockResponse)(nil), // 3: chainrpc.GetBestBlockResponse - (*GetBlockHashRequest)(nil), // 4: chainrpc.GetBlockHashRequest - (*GetBlockHashResponse)(nil), // 5: chainrpc.GetBlockHashResponse + (*GetBlockRequest)(nil), // 0: chainrpc.GetBlockRequest + (*GetBlockResponse)(nil), // 1: chainrpc.GetBlockResponse + (*GetBlockHeaderRequest)(nil), // 2: chainrpc.GetBlockHeaderRequest + (*GetBlockHeaderResponse)(nil), // 3: chainrpc.GetBlockHeaderResponse + (*GetBestBlockRequest)(nil), // 4: chainrpc.GetBestBlockRequest + (*GetBestBlockResponse)(nil), // 5: chainrpc.GetBestBlockResponse + (*GetBlockHashRequest)(nil), // 6: chainrpc.GetBlockHashRequest + (*GetBlockHashResponse)(nil), // 7: chainrpc.GetBlockHashResponse } var file_chainrpc_chainkit_proto_depIdxs = []int32{ 0, // 0: chainrpc.ChainKit.GetBlock:input_type -> chainrpc.GetBlockRequest - 2, // 1: chainrpc.ChainKit.GetBestBlock:input_type -> chainrpc.GetBestBlockRequest - 4, // 2: chainrpc.ChainKit.GetBlockHash:input_type -> chainrpc.GetBlockHashRequest - 1, // 3: chainrpc.ChainKit.GetBlock:output_type -> chainrpc.GetBlockResponse - 3, // 4: chainrpc.ChainKit.GetBestBlock:output_type -> chainrpc.GetBestBlockResponse - 5, // 5: chainrpc.ChainKit.GetBlockHash:output_type -> chainrpc.GetBlockHashResponse - 3, // [3:6] is the sub-list for method output_type - 0, // [0:3] is the sub-list for method input_type + 2, // 1: chainrpc.ChainKit.GetBlockHeader:input_type -> chainrpc.GetBlockHeaderRequest + 4, // 2: chainrpc.ChainKit.GetBestBlock:input_type -> chainrpc.GetBestBlockRequest + 6, // 3: chainrpc.ChainKit.GetBlockHash:input_type -> chainrpc.GetBlockHashRequest + 1, // 4: chainrpc.ChainKit.GetBlock:output_type -> chainrpc.GetBlockResponse + 3, // 5: chainrpc.ChainKit.GetBlockHeader:output_type -> chainrpc.GetBlockHeaderResponse + 5, // 6: chainrpc.ChainKit.GetBestBlock:output_type -> chainrpc.GetBestBlockResponse + 7, // 7: chainrpc.ChainKit.GetBlockHash:output_type -> chainrpc.GetBlockHashResponse + 4, // [4:8] is the sub-list for method output_type + 0, // [0:4] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -421,7 +535,7 @@ func file_chainrpc_chainkit_proto_init() { } } file_chainrpc_chainkit_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetBestBlockRequest); i { + switch v := v.(*GetBlockHeaderRequest); i { case 0: return &v.state case 1: @@ -433,7 +547,7 @@ func file_chainrpc_chainkit_proto_init() { } } file_chainrpc_chainkit_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetBestBlockResponse); i { + switch v := v.(*GetBlockHeaderResponse); i { case 0: return &v.state case 1: @@ -445,7 +559,7 @@ func file_chainrpc_chainkit_proto_init() { } } file_chainrpc_chainkit_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetBlockHashRequest); i { + switch v := v.(*GetBestBlockRequest); i { case 0: return &v.state case 1: @@ -457,6 +571,30 @@ func file_chainrpc_chainkit_proto_init() { } } file_chainrpc_chainkit_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBestBlockResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_chainrpc_chainkit_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBlockHashRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_chainrpc_chainkit_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetBlockHashResponse); i { case 0: return &v.state @@ -475,7 +613,7 @@ func file_chainrpc_chainkit_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_chainrpc_chainkit_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 8, NumExtensions: 0, NumServices: 1, }, diff --git a/lnrpc/chainrpc/chainkit.pb.gw.go b/lnrpc/chainrpc/chainkit.pb.gw.go index d49ed54ff5..665f632953 100644 --- a/lnrpc/chainrpc/chainkit.pb.gw.go +++ b/lnrpc/chainrpc/chainkit.pb.gw.go @@ -67,6 +67,42 @@ func local_request_ChainKit_GetBlock_0(ctx context.Context, marshaler runtime.Ma } +var ( + filter_ChainKit_GetBlockHeader_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_ChainKit_GetBlockHeader_0(ctx context.Context, marshaler runtime.Marshaler, client ChainKitClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetBlockHeaderRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ChainKit_GetBlockHeader_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetBlockHeader(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ChainKit_GetBlockHeader_0(ctx context.Context, marshaler runtime.Marshaler, server ChainKitServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetBlockHeaderRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ChainKit_GetBlockHeader_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetBlockHeader(ctx, &protoReq) + return msg, metadata, err + +} + func request_ChainKit_GetBestBlock_0(ctx context.Context, marshaler runtime.Marshaler, client ChainKitClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq GetBestBlockRequest var metadata runtime.ServerMetadata @@ -150,6 +186,29 @@ func RegisterChainKitHandlerServer(ctx context.Context, mux *runtime.ServeMux, s }) + mux.Handle("GET", pattern_ChainKit_GetBlockHeader_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/chainrpc.ChainKit/GetBlockHeader", runtime.WithHTTPPathPattern("/v2/chainkit/blockheader")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ChainKit_GetBlockHeader_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ChainKit_GetBlockHeader_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_ChainKit_GetBestBlock_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -257,6 +316,26 @@ func RegisterChainKitHandlerClient(ctx context.Context, mux *runtime.ServeMux, c }) + mux.Handle("GET", pattern_ChainKit_GetBlockHeader_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/chainrpc.ChainKit/GetBlockHeader", runtime.WithHTTPPathPattern("/v2/chainkit/blockheader")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ChainKit_GetBlockHeader_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ChainKit_GetBlockHeader_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_ChainKit_GetBestBlock_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -303,6 +382,8 @@ func RegisterChainKitHandlerClient(ctx context.Context, mux *runtime.ServeMux, c var ( pattern_ChainKit_GetBlock_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "chainkit", "block"}, "")) + pattern_ChainKit_GetBlockHeader_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "chainkit", "blockheader"}, "")) + pattern_ChainKit_GetBestBlock_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "chainkit", "bestblock"}, "")) pattern_ChainKit_GetBlockHash_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "chainkit", "blockhash"}, "")) @@ -311,6 +392,8 @@ var ( var ( forward_ChainKit_GetBlock_0 = runtime.ForwardResponseMessage + forward_ChainKit_GetBlockHeader_0 = runtime.ForwardResponseMessage + forward_ChainKit_GetBestBlock_0 = runtime.ForwardResponseMessage forward_ChainKit_GetBlockHash_0 = runtime.ForwardResponseMessage diff --git a/lnrpc/chainrpc/chainkit.pb.json.go b/lnrpc/chainrpc/chainkit.pb.json.go index e05648a2fb..8979b593c8 100644 --- a/lnrpc/chainrpc/chainkit.pb.json.go +++ b/lnrpc/chainrpc/chainkit.pb.json.go @@ -46,6 +46,31 @@ func RegisterChainKitJSONCallbacks(registry map[string]func(ctx context.Context, callback(string(respBytes), nil) } + registry["chainrpc.ChainKit.GetBlockHeader"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &GetBlockHeaderRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewChainKitClient(conn) + resp, err := client.GetBlockHeader(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } + registry["chainrpc.ChainKit.GetBestBlock"] = func(ctx context.Context, conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { diff --git a/lnrpc/chainrpc/chainkit.proto b/lnrpc/chainrpc/chainkit.proto index 8db7c52d27..150e0563d6 100644 --- a/lnrpc/chainrpc/chainkit.proto +++ b/lnrpc/chainrpc/chainkit.proto @@ -12,6 +12,11 @@ service ChainKit { */ rpc GetBlock (GetBlockRequest) returns (GetBlockResponse); + /* lncli: `chain getblockheader` + GetBlockHeader returns a block header with a particular block hash. + */ + rpc GetBlockHeader (GetBlockHeaderRequest) returns (GetBlockHeaderResponse); + /* lncli: `chain getbestblock` GetBestBlock returns the block hash and current height from the valid most-work chain. @@ -37,6 +42,16 @@ message GetBlockResponse { bytes raw_block = 1; } +message GetBlockHeaderRequest { + // The hash of the block with the requested header. + bytes block_hash = 1; +} + +message GetBlockHeaderResponse { + // The header of the block with the requested hash. + bytes raw_block_header = 1; +} + message GetBestBlockRequest { } diff --git a/lnrpc/chainrpc/chainkit.swagger.json b/lnrpc/chainrpc/chainkit.swagger.json index c5bae06c06..c3cbeb6dfd 100644 --- a/lnrpc/chainrpc/chainkit.swagger.json +++ b/lnrpc/chainrpc/chainkit.swagger.json @@ -104,6 +104,39 @@ "ChainKit" ] } + }, + "/v2/chainkit/blockheader": { + "get": { + "summary": "lncli: `chain getblockheader`\nGetBlockHeader returns a block header with a particular block hash.", + "operationId": "ChainKit_GetBlockHeader", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/chainrpcGetBlockHeaderResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "block_hash", + "description": "The hash of the block with the requested header.", + "in": "query", + "required": false, + "type": "string", + "format": "byte" + } + ], + "tags": [ + "ChainKit" + ] + } } }, "definitions": { @@ -132,6 +165,16 @@ } } }, + "chainrpcGetBlockHeaderResponse": { + "type": "object", + "properties": { + "raw_block_header": { + "type": "string", + "format": "byte", + "description": "The header of the block with the requested hash." + } + } + }, "chainrpcGetBlockResponse": { "type": "object", "properties": { diff --git a/lnrpc/chainrpc/chainkit.yaml b/lnrpc/chainrpc/chainkit.yaml index 815dfc5a78..2241233c8c 100644 --- a/lnrpc/chainrpc/chainkit.yaml +++ b/lnrpc/chainrpc/chainkit.yaml @@ -5,6 +5,8 @@ http: rules: - selector: chainrpc.ChainKit.GetBlock get: "/v2/chainkit/block" + - selector: chainrpc.ChainKit.GetBlockHeader + get: "/v2/chainkit/blockheader" - selector: chainrpc.ChainKit.GetBestBlock get: "/v2/chainkit/bestblock" - selector: chainrpc.ChainKit.GetBlockHash diff --git a/lnrpc/chainrpc/chainkit_grpc.pb.go b/lnrpc/chainrpc/chainkit_grpc.pb.go index 47585f7f36..c92c504775 100644 --- a/lnrpc/chainrpc/chainkit_grpc.pb.go +++ b/lnrpc/chainrpc/chainkit_grpc.pb.go @@ -21,6 +21,9 @@ type ChainKitClient interface { // lncli: `chain getblock` // GetBlock returns a block given the corresponding block hash. GetBlock(ctx context.Context, in *GetBlockRequest, opts ...grpc.CallOption) (*GetBlockResponse, error) + // lncli: `chain getblockheader` + // GetBlockHeader returns a block header with a particular block hash. + GetBlockHeader(ctx context.Context, in *GetBlockHeaderRequest, opts ...grpc.CallOption) (*GetBlockHeaderResponse, error) // lncli: `chain getbestblock` // GetBestBlock returns the block hash and current height from the valid // most-work chain. @@ -48,6 +51,15 @@ func (c *chainKitClient) GetBlock(ctx context.Context, in *GetBlockRequest, opts return out, nil } +func (c *chainKitClient) GetBlockHeader(ctx context.Context, in *GetBlockHeaderRequest, opts ...grpc.CallOption) (*GetBlockHeaderResponse, error) { + out := new(GetBlockHeaderResponse) + err := c.cc.Invoke(ctx, "/chainrpc.ChainKit/GetBlockHeader", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *chainKitClient) GetBestBlock(ctx context.Context, in *GetBestBlockRequest, opts ...grpc.CallOption) (*GetBestBlockResponse, error) { out := new(GetBestBlockResponse) err := c.cc.Invoke(ctx, "/chainrpc.ChainKit/GetBestBlock", in, out, opts...) @@ -73,6 +85,9 @@ type ChainKitServer interface { // lncli: `chain getblock` // GetBlock returns a block given the corresponding block hash. GetBlock(context.Context, *GetBlockRequest) (*GetBlockResponse, error) + // lncli: `chain getblockheader` + // GetBlockHeader returns a block header with a particular block hash. + GetBlockHeader(context.Context, *GetBlockHeaderRequest) (*GetBlockHeaderResponse, error) // lncli: `chain getbestblock` // GetBestBlock returns the block hash and current height from the valid // most-work chain. @@ -91,6 +106,9 @@ type UnimplementedChainKitServer struct { func (UnimplementedChainKitServer) GetBlock(context.Context, *GetBlockRequest) (*GetBlockResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetBlock not implemented") } +func (UnimplementedChainKitServer) GetBlockHeader(context.Context, *GetBlockHeaderRequest) (*GetBlockHeaderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetBlockHeader not implemented") +} func (UnimplementedChainKitServer) GetBestBlock(context.Context, *GetBestBlockRequest) (*GetBestBlockResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetBestBlock not implemented") } @@ -128,6 +146,24 @@ func _ChainKit_GetBlock_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } +func _ChainKit_GetBlockHeader_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetBlockHeaderRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ChainKitServer).GetBlockHeader(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/chainrpc.ChainKit/GetBlockHeader", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ChainKitServer).GetBlockHeader(ctx, req.(*GetBlockHeaderRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _ChainKit_GetBestBlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetBestBlockRequest) if err := dec(in); err != nil { @@ -175,6 +211,10 @@ var ChainKit_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetBlock", Handler: _ChainKit_GetBlock_Handler, }, + { + MethodName: "GetBlockHeader", + Handler: _ChainKit_GetBlockHeader_Handler, + }, { MethodName: "GetBestBlock", Handler: _ChainKit_GetBestBlock_Handler, diff --git a/lntest/mock/chainio.go b/lntest/mock/chainio.go index 1f07056f06..4dd0868b21 100644 --- a/lntest/mock/chainio.go +++ b/lntest/mock/chainio.go @@ -32,3 +32,10 @@ func (c *ChainIO) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) { func (c *ChainIO) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) { return nil, nil } + +// GetBlockHeader currently returns dummy values. +func (c *ChainIO) GetBlockHeader(blockHash *chainhash.Hash) (*wire.BlockHeader, + error) { + + return nil, nil +} diff --git a/lntest/rpc/chain_kit.go b/lntest/rpc/chain_kit.go index c668b3a0fe..453de276a9 100644 --- a/lntest/rpc/chain_kit.go +++ b/lntest/rpc/chain_kit.go @@ -45,6 +45,24 @@ func (h *HarnessRPC) GetBlock( return resp } +// GetBlockHeader makes an RPC call to chain kit client's GetBlockHeader and +// asserts. +func (h *HarnessRPC) GetBlockHeader( + req *chainrpc.GetBlockHeaderRequest) *chainrpc.GetBlockHeaderResponse { + + if req == nil { + req = &chainrpc.GetBlockHeaderRequest{} + } + + ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout) + defer cancel() + + resp, err := h.ChainKit.GetBlockHeader(ctxt, req) + h.NoError(err, "GetBlockHeader") + + return resp +} + // GetBlockHash makes an RPC call to chain kit client's GetBlockHash and // asserts. func (h *HarnessRPC) GetBlockHash( diff --git a/lnwallet/btcwallet/blockchain.go b/lnwallet/btcwallet/blockchain.go index 5cb0886f09..25b51d5e06 100644 --- a/lnwallet/btcwallet/blockchain.go +++ b/lnwallet/btcwallet/blockchain.go @@ -149,6 +149,15 @@ func (b *BtcWallet) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) return b.chain.GetBlock(blockHash) } +// GetBlockHeader returns a block header for the block with the given hash. +// +// This method is a part of the lnwallet.BlockChainIO interface. +func (b *BtcWallet) GetBlockHeader( + blockHash *chainhash.Hash) (*wire.BlockHeader, error) { + + return b.chain.GetBlockHeader(blockHash) +} + // GetBlockHash returns the hash of the block in the best blockchain at the // given height. // diff --git a/lnwallet/interface.go b/lnwallet/interface.go index d8dc9d5b8b..e8dd72dc07 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -562,6 +562,9 @@ type BlockChainIO interface { // GetBlock returns the block in the main chain identified by the given // hash. GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) + + // GetBlockHeader returns the block header for the given block hash. + GetBlockHeader(blockHash *chainhash.Hash) (*wire.BlockHeader, error) } // MessageSigner represents an abstract object capable of signing arbitrary diff --git a/lnwallet/mock.go b/lnwallet/mock.go index 597be66189..6d382a58eb 100644 --- a/lnwallet/mock.go +++ b/lnwallet/mock.go @@ -364,3 +364,9 @@ func (*mockChainIO) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, return nil, nil } + +func (*mockChainIO) GetBlockHeader( + blockHash *chainhash.Hash) (*wire.BlockHeader, error) { + + return nil, nil +} diff --git a/routing/notifications_test.go b/routing/notifications_test.go index 658df54233..00bc29f6bc 100644 --- a/routing/notifications_test.go +++ b/routing/notifications_test.go @@ -228,6 +228,20 @@ func (m *mockChain) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) return block, nil } +func (m *mockChain) GetBlockHeader( + blockHash *chainhash.Hash) (*wire.BlockHeader, error) { + + m.RLock() + defer m.RUnlock() + + block, ok := m.blocks[*blockHash] + if !ok { + return nil, fmt.Errorf("block not found") + } + + return &block.Header, nil +} + type mockChainView struct { sync.RWMutex