From 03f9549f97c0ec51a4dea4736a2829affe328907 Mon Sep 17 00:00:00 2001 From: ffranr Date: Tue, 22 Nov 2022 14:30:47 +0000 Subject: [PATCH 1/6] chainrpc: generalise chain notifier RPC server name --- lnrpc/chainrpc/{chainnotifier_server.go => chain_server.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lnrpc/chainrpc/{chainnotifier_server.go => chain_server.go} (100%) diff --git a/lnrpc/chainrpc/chainnotifier_server.go b/lnrpc/chainrpc/chain_server.go similarity index 100% rename from lnrpc/chainrpc/chainnotifier_server.go rename to lnrpc/chainrpc/chain_server.go From 308e7ce72d72dbb9c61fb6d768d0305bfcce2228 Mon Sep 17 00:00:00 2001 From: ffranr Date: Tue, 22 Nov 2022 19:17:02 +0000 Subject: [PATCH 2/6] lnrpc: identify *.proto files as separate files within a given directory --- lnrpc/gen_protos.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnrpc/gen_protos.sh b/lnrpc/gen_protos.sh index 83f9871cc..9919c11b5 100755 --- a/lnrpc/gen_protos.sh +++ b/lnrpc/gen_protos.sh @@ -67,7 +67,7 @@ function generate() { --plugin=protoc-gen-custom=$falafel\ --custom_out=. \ --custom_opt="$opts" \ - "$(find . -name '*.proto')" + $(find . -name '*.proto') popd done } From 561bf829857a51c1d1c2f6fea18eac8d6726143e Mon Sep 17 00:00:00 2001 From: ffranr Date: Thu, 1 Dec 2022 17:12:51 +0000 Subject: [PATCH 3/6] chainrpc: add chainkit RPC service New endpoints: GetBlock, GetBestBlock, and GetBlockHash. --- lnrpc/chainrpc/chain_server.go | 102 +++++- lnrpc/chainrpc/chainkit.pb.go | 490 +++++++++++++++++++++++++++ lnrpc/chainrpc/chainkit.pb.gw.go | 317 +++++++++++++++++ lnrpc/chainrpc/chainkit.pb.json.go | 98 ++++++ lnrpc/chainrpc/chainkit.proto | 59 ++++ lnrpc/chainrpc/chainkit.swagger.json | 177 ++++++++++ lnrpc/chainrpc/chainkit.yaml | 11 + lnrpc/chainrpc/chainkit_grpc.pb.go | 189 +++++++++++ lnrpc/chainrpc/config_active.go | 4 + lnrpc/chainrpc/driver.go | 5 + lntemp/rpc/harness_rpc.go | 2 + subrpcserver_config.go | 3 + 12 files changed, 1450 insertions(+), 7 deletions(-) create mode 100644 lnrpc/chainrpc/chainkit.pb.go create mode 100644 lnrpc/chainrpc/chainkit.pb.gw.go create mode 100644 lnrpc/chainrpc/chainkit.pb.json.go create mode 100644 lnrpc/chainrpc/chainkit.proto create mode 100644 lnrpc/chainrpc/chainkit.swagger.json create mode 100644 lnrpc/chainrpc/chainkit.yaml create mode 100644 lnrpc/chainrpc/chainkit_grpc.pb.go diff --git a/lnrpc/chainrpc/chain_server.go b/lnrpc/chainrpc/chain_server.go index 67e6c5057..38ef358d6 100644 --- a/lnrpc/chainrpc/chain_server.go +++ b/lnrpc/chainrpc/chain_server.go @@ -42,6 +42,18 @@ var ( // macPermissions maps RPC calls to the permissions they require. macPermissions = map[string][]bakery.Op{ + "/chainrpc.ChainKit/GetBlock": {{ + Entity: "onchain", + Action: "read", + }}, + "/chainrpc.ChainKit/GetBestBlock": {{ + Entity: "onchain", + Action: "read", + }}, + "/chainrpc.ChainKit/GetBlockHash": {{ + Entity: "onchain", + Action: "read", + }}, "/chainrpc.ChainNotifier/RegisterConfirmationsNtfn": {{ Entity: "onchain", Action: "read", @@ -77,17 +89,19 @@ var ( // It is used to register the gRPC sub-server with the root server before we // have the necessary dependencies to populate the actual sub-server. type ServerShell struct { + ChainKitServer ChainNotifierServer } -// Server is a sub-server of the main RPC server: the chain notifier RPC. This -// RPC sub-server allows external callers to access the full chain notifier -// capabilities of lnd. This allows callers to create custom protocols, external -// to lnd, even backed by multiple distinct lnd across independent failure -// domains. +// Server is a sub-server of the main RPC server. It serves the chainkit RPC +// and chain notifier RPC. This RPC sub-server allows external callers to access +// the full chainkit and chain notifier capabilities of lnd. This allows callers +// to create custom protocols, external to lnd, even backed by multiple distinct +// lnd across independent failure domains. type Server struct { // Required by the grpc-gateway/v2 library for forward compatibility. UnimplementedChainNotifierServer + UnimplementedChainKitServer started sync.Once stopped sync.Once @@ -149,8 +163,10 @@ func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) { } // Compile-time checks to ensure that Server fully implements the -// ChainNotifierServer gRPC service and lnrpc.SubServer interface. +// ChainNotifierServer gRPC service, ChainKitServer gRPC service, and +// lnrpc.SubServer interface. var _ ChainNotifierServer = (*Server)(nil) +var _ ChainKitServer = (*Server)(nil) var _ lnrpc.SubServer = (*Server)(nil) // Start launches any helper goroutines required for the server to function. @@ -188,10 +204,13 @@ func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error { // We make sure that we register it with the main gRPC server to ensure // all our methods are routed properly. RegisterChainNotifierServer(grpcServer, r) - log.Debug("ChainNotifier RPC server successfully register with root " + "gRPC server") + RegisterChainKitServer(grpcServer, r) + log.Debug("ChainKit RPC server successfully register with root gRPC " + + "server") + return nil } @@ -214,6 +233,18 @@ func (r *ServerShell) RegisterWithRestServer(ctx context.Context, log.Debugf("ChainNotifier REST server successfully registered with " + "root REST server") + + // Register chainkit with the main REST server to ensure all our methods + // are routed properly. + err = RegisterChainKitHandlerFromEndpoint(ctx, mux, dest, opts) + if err != nil { + log.Errorf("Could not register ChainKit REST server with root "+ + "REST server: %v", err) + return err + } + log.Debugf("ChainKit REST server successfully registered with root " + + "REST server") + return nil } @@ -233,9 +264,66 @@ func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispat } r.ChainNotifierServer = subServer + r.ChainKitServer = subServer return subServer, macPermissions, nil } +// GetBlock returns a block given the corresponding block hash. +func (s *Server) GetBlock(ctx context.Context, + in *GetBlockRequest) (*GetBlockResponse, error) { + + // We'll start by reconstructing the RPC request into what the + // underlying chain functionality expects. + var blockHash chainhash.Hash + copy(blockHash[:], in.BlockHash) + + block, err := s.cfg.Chain.GetBlock(&blockHash) + if err != nil { + return nil, err + } + + // Serialize block for RPC response. + var blockBuf bytes.Buffer + err = block.Serialize(&blockBuf) + if err != nil { + return nil, err + } + rawBlock := blockBuf.Bytes() + + return &GetBlockResponse{RawBlock: rawBlock}, nil +} + +// GetBestBlock returns the latest block hash and current height of the valid +// most-work chain. +func (s *Server) GetBestBlock(ctx context.Context, + req *GetBestBlockRequest) (*GetBestBlockResponse, error) { + + blockHash, blockHeight, err := s.cfg.Chain.GetBestBlock() + if err != nil { + return nil, err + } + + return &GetBestBlockResponse{ + BlockHash: blockHash[:], + BlockHeight: blockHeight, + }, nil +} + +// GetBlockHash returns the hash of the block in the best blockchain +// at the given height. +func (s *Server) GetBlockHash(ctx context.Context, + req *GetBlockHashRequest) (*GetBlockHashResponse, error) { + + blockHash, err := s.cfg.Chain.GetBlockHash(req.BlockHeight) + if err != nil { + return nil, err + } + + return &GetBlockHashResponse{ + BlockHash: blockHash[:], + }, nil +} + // RegisterConfirmationsNtfn is a synchronous response-streaming RPC that // registers an intent for a client to be notified once a confirmation request // has reached its required number of confirmations on-chain. diff --git a/lnrpc/chainrpc/chainkit.pb.go b/lnrpc/chainrpc/chainkit.pb.go new file mode 100644 index 000000000..be6eafe4d --- /dev/null +++ b/lnrpc/chainrpc/chainkit.pb.go @@ -0,0 +1,490 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.6.1 +// source: chainrpc/chainkit.proto + +package chainrpc + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GetBlockRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The hash of the requested block. + BlockHash []byte `protobuf:"bytes,1,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"` +} + +func (x *GetBlockRequest) Reset() { + *x = GetBlockRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_chainrpc_chainkit_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlockRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlockRequest) ProtoMessage() {} + +func (x *GetBlockRequest) ProtoReflect() protoreflect.Message { + mi := &file_chainrpc_chainkit_proto_msgTypes[0] + 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 GetBlockRequest.ProtoReflect.Descriptor instead. +func (*GetBlockRequest) Descriptor() ([]byte, []int) { + return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{0} +} + +func (x *GetBlockRequest) GetBlockHash() []byte { + if x != nil { + return x.BlockHash + } + return nil +} + +// TODO(ffranr): The neutrino GetBlock response includes many +// additional helpful fields. Consider adding them here also. +type GetBlockResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The raw bytes of the requested block. + RawBlock []byte `protobuf:"bytes,1,opt,name=raw_block,json=rawBlock,proto3" json:"raw_block,omitempty"` +} + +func (x *GetBlockResponse) Reset() { + *x = GetBlockResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_chainrpc_chainkit_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlockResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlockResponse) ProtoMessage() {} + +func (x *GetBlockResponse) ProtoReflect() protoreflect.Message { + mi := &file_chainrpc_chainkit_proto_msgTypes[1] + 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 GetBlockResponse.ProtoReflect.Descriptor instead. +func (*GetBlockResponse) Descriptor() ([]byte, []int) { + return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{1} +} + +func (x *GetBlockResponse) GetRawBlock() []byte { + if x != nil { + return x.RawBlock + } + return nil +} + +type GetBestBlockRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetBestBlockRequest) Reset() { + *x = GetBestBlockRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_chainrpc_chainkit_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBestBlockRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBestBlockRequest) ProtoMessage() {} + +func (x *GetBestBlockRequest) 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 GetBestBlockRequest.ProtoReflect.Descriptor instead. +func (*GetBestBlockRequest) Descriptor() ([]byte, []int) { + return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{2} +} + +type GetBestBlockResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The hash of the best block. + BlockHash []byte `protobuf:"bytes,1,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"` + // The height of the best block. + BlockHeight int32 `protobuf:"varint,2,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` +} + +func (x *GetBestBlockResponse) Reset() { + *x = GetBestBlockResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_chainrpc_chainkit_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBestBlockResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBestBlockResponse) ProtoMessage() {} + +func (x *GetBestBlockResponse) 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 GetBestBlockResponse.ProtoReflect.Descriptor instead. +func (*GetBestBlockResponse) Descriptor() ([]byte, []int) { + return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{3} +} + +func (x *GetBestBlockResponse) GetBlockHash() []byte { + if x != nil { + return x.BlockHash + } + return nil +} + +func (x *GetBestBlockResponse) GetBlockHeight() int32 { + if x != nil { + return x.BlockHeight + } + return 0 +} + +type GetBlockHashRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Block height of the target best chain block. + BlockHeight int64 `protobuf:"varint,1,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` +} + +func (x *GetBlockHashRequest) Reset() { + *x = GetBlockHashRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_chainrpc_chainkit_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlockHashRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlockHashRequest) ProtoMessage() {} + +func (x *GetBlockHashRequest) ProtoReflect() protoreflect.Message { + mi := &file_chainrpc_chainkit_proto_msgTypes[4] + 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 GetBlockHashRequest.ProtoReflect.Descriptor instead. +func (*GetBlockHashRequest) Descriptor() ([]byte, []int) { + return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{4} +} + +func (x *GetBlockHashRequest) GetBlockHeight() int64 { + if x != nil { + return x.BlockHeight + } + return 0 +} + +type GetBlockHashResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The hash of the best block at the specified height. + BlockHash []byte `protobuf:"bytes,1,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"` +} + +func (x *GetBlockHashResponse) Reset() { + *x = GetBlockHashResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_chainrpc_chainkit_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlockHashResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlockHashResponse) ProtoMessage() {} + +func (x *GetBlockHashResponse) ProtoReflect() protoreflect.Message { + mi := &file_chainrpc_chainkit_proto_msgTypes[5] + 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 GetBlockHashResponse.ProtoReflect.Descriptor instead. +func (*GetBlockHashResponse) Descriptor() ([]byte, []int) { + return file_chainrpc_chainkit_proto_rawDescGZIP(), []int{5} +} + +func (x *GetBlockHashResponse) GetBlockHash() []byte { + if x != nil { + return x.BlockHash + } + return nil +} + +var File_chainrpc_chainkit_proto protoreflect.FileDescriptor + +var file_chainrpc_chainkit_proto_rawDesc = []byte{ + 0x0a, 0x17, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x6b, 0x69, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x72, 0x70, 0x63, 0x22, 0x30, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 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, 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, + 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, + 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, 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, +} + +var ( + file_chainrpc_chainkit_proto_rawDescOnce sync.Once + file_chainrpc_chainkit_proto_rawDescData = file_chainrpc_chainkit_proto_rawDesc +) + +func file_chainrpc_chainkit_proto_rawDescGZIP() []byte { + file_chainrpc_chainkit_proto_rawDescOnce.Do(func() { + file_chainrpc_chainkit_proto_rawDescData = protoimpl.X.CompressGZIP(file_chainrpc_chainkit_proto_rawDescData) + }) + return file_chainrpc_chainkit_proto_rawDescData +} + +var file_chainrpc_chainkit_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +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 +} +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 + 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 +} + +func init() { file_chainrpc_chainkit_proto_init() } +func file_chainrpc_chainkit_proto_init() { + if File_chainrpc_chainkit_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_chainrpc_chainkit_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBlockRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_chainrpc_chainkit_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBlockResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_chainrpc_chainkit_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBestBlockRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_chainrpc_chainkit_proto_msgTypes[3].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[4].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[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBlockHashResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_chainrpc_chainkit_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_chainrpc_chainkit_proto_goTypes, + DependencyIndexes: file_chainrpc_chainkit_proto_depIdxs, + MessageInfos: file_chainrpc_chainkit_proto_msgTypes, + }.Build() + File_chainrpc_chainkit_proto = out.File + file_chainrpc_chainkit_proto_rawDesc = nil + file_chainrpc_chainkit_proto_goTypes = nil + file_chainrpc_chainkit_proto_depIdxs = nil +} diff --git a/lnrpc/chainrpc/chainkit.pb.gw.go b/lnrpc/chainrpc/chainkit.pb.gw.go new file mode 100644 index 000000000..d49ed54ff --- /dev/null +++ b/lnrpc/chainrpc/chainkit.pb.gw.go @@ -0,0 +1,317 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: chainrpc/chainkit.proto + +/* +Package chainrpc is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package chainrpc + +import ( + "context" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = metadata.Join + +var ( + filter_ChainKit_GetBlock_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_ChainKit_GetBlock_0(ctx context.Context, marshaler runtime.Marshaler, client ChainKitClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetBlockRequest + 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_GetBlock_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetBlock(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ChainKit_GetBlock_0(ctx context.Context, marshaler runtime.Marshaler, server ChainKitServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetBlockRequest + 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_GetBlock_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetBlock(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 + + msg, err := client.GetBestBlock(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ChainKit_GetBestBlock_0(ctx context.Context, marshaler runtime.Marshaler, server ChainKitServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetBestBlockRequest + var metadata runtime.ServerMetadata + + msg, err := server.GetBestBlock(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_ChainKit_GetBlockHash_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_ChainKit_GetBlockHash_0(ctx context.Context, marshaler runtime.Marshaler, client ChainKitClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetBlockHashRequest + 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_GetBlockHash_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetBlockHash(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ChainKit_GetBlockHash_0(ctx context.Context, marshaler runtime.Marshaler, server ChainKitServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetBlockHashRequest + 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_GetBlockHash_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetBlockHash(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterChainKitHandlerServer registers the http handlers for service ChainKit to "mux". +// UnaryRPC :call ChainKitServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterChainKitHandlerFromEndpoint instead. +func RegisterChainKitHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ChainKitServer) error { + + mux.Handle("GET", pattern_ChainKit_GetBlock_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/GetBlock", runtime.WithHTTPPathPattern("/v2/chainkit/block")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ChainKit_GetBlock_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_GetBlock_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() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/chainrpc.ChainKit/GetBestBlock", runtime.WithHTTPPathPattern("/v2/chainkit/bestblock")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ChainKit_GetBestBlock_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_GetBestBlock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_ChainKit_GetBlockHash_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/GetBlockHash", runtime.WithHTTPPathPattern("/v2/chainkit/blockhash")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ChainKit_GetBlockHash_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_GetBlockHash_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterChainKitHandlerFromEndpoint is same as RegisterChainKitHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterChainKitHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterChainKitHandler(ctx, mux, conn) +} + +// RegisterChainKitHandler registers the http handlers for service ChainKit to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterChainKitHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterChainKitHandlerClient(ctx, mux, NewChainKitClient(conn)) +} + +// RegisterChainKitHandlerClient registers the http handlers for service ChainKit +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ChainKitClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ChainKitClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "ChainKitClient" to call the correct interceptors. +func RegisterChainKitHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ChainKitClient) error { + + mux.Handle("GET", pattern_ChainKit_GetBlock_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/GetBlock", runtime.WithHTTPPathPattern("/v2/chainkit/block")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ChainKit_GetBlock_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_GetBlock_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() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/chainrpc.ChainKit/GetBestBlock", runtime.WithHTTPPathPattern("/v2/chainkit/bestblock")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ChainKit_GetBestBlock_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_GetBestBlock_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_ChainKit_GetBlockHash_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/GetBlockHash", runtime.WithHTTPPathPattern("/v2/chainkit/blockhash")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ChainKit_GetBlockHash_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_GetBlockHash_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_ChainKit_GetBlock_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "chainkit", "block"}, "")) + + 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"}, "")) +) + +var ( + forward_ChainKit_GetBlock_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 new file mode 100644 index 000000000..e05648a2f --- /dev/null +++ b/lnrpc/chainrpc/chainkit.pb.json.go @@ -0,0 +1,98 @@ +// Code generated by falafel 0.9.1. DO NOT EDIT. +// source: chainkit.proto + +package chainrpc + +import ( + "context" + + gateway "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "google.golang.org/grpc" + "google.golang.org/protobuf/encoding/protojson" +) + +func RegisterChainKitJSONCallbacks(registry map[string]func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error))) { + + marshaler := &gateway.JSONPb{ + MarshalOptions: protojson.MarshalOptions{ + UseProtoNames: true, + EmitUnpopulated: true, + }, + } + + registry["chainrpc.ChainKit.GetBlock"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &GetBlockRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewChainKitClient(conn) + resp, err := client.GetBlock(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)) { + + req := &GetBestBlockRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewChainKitClient(conn) + resp, err := client.GetBestBlock(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.GetBlockHash"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &GetBlockHashRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewChainKitClient(conn) + resp, err := client.GetBlockHash(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } +} diff --git a/lnrpc/chainrpc/chainkit.proto b/lnrpc/chainrpc/chainkit.proto new file mode 100644 index 000000000..8db7c52d2 --- /dev/null +++ b/lnrpc/chainrpc/chainkit.proto @@ -0,0 +1,59 @@ +syntax = "proto3"; + +package chainrpc; + +option go_package = "github.com/lightningnetwork/lnd/lnrpc/chainrpc"; + +// ChainKit is a service that can be used to get information from the +// chain backend. +service ChainKit { + /* lncli: `chain getblock` + GetBlock returns a block given the corresponding block hash. + */ + rpc GetBlock (GetBlockRequest) returns (GetBlockResponse); + + /* lncli: `chain getbestblock` + GetBestBlock returns the block hash and current height from the valid + most-work chain. + */ + rpc GetBestBlock (GetBestBlockRequest) returns (GetBestBlockResponse); + + /* lncli: `chain getblockhash` + GetBlockHash returns the hash of the block in the best blockchain + at the given height. + */ + rpc GetBlockHash (GetBlockHashRequest) returns (GetBlockHashResponse); +} + +message GetBlockRequest { + // The hash of the requested block. + bytes block_hash = 1; +} + +// TODO(ffranr): The neutrino GetBlock response includes many +// additional helpful fields. Consider adding them here also. +message GetBlockResponse { + // The raw bytes of the requested block. + bytes raw_block = 1; +} + +message GetBestBlockRequest { +} + +message GetBestBlockResponse { + // The hash of the best block. + bytes block_hash = 1; + + // The height of the best block. + int32 block_height = 2; +} + +message GetBlockHashRequest { + // Block height of the target best chain block. + int64 block_height = 1; +} + +message GetBlockHashResponse { + // The hash of the best block at the specified height. + bytes block_hash = 1; +} \ No newline at end of file diff --git a/lnrpc/chainrpc/chainkit.swagger.json b/lnrpc/chainrpc/chainkit.swagger.json new file mode 100644 index 000000000..c5bae06c0 --- /dev/null +++ b/lnrpc/chainrpc/chainkit.swagger.json @@ -0,0 +1,177 @@ +{ + "swagger": "2.0", + "info": { + "title": "chainrpc/chainkit.proto", + "version": "version not set" + }, + "tags": [ + { + "name": "ChainKit" + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/v2/chainkit/bestblock": { + "get": { + "summary": "lncli: `chain getbestblock`\nGetBestBlock returns the block hash and current height from the valid\nmost-work chain.", + "operationId": "ChainKit_GetBestBlock", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/chainrpcGetBestBlockResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "ChainKit" + ] + } + }, + "/v2/chainkit/block": { + "get": { + "summary": "lncli: `chain getblock`\nGetBlock returns a block given the corresponding block hash.", + "operationId": "ChainKit_GetBlock", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/chainrpcGetBlockResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "block_hash", + "description": "The hash of the requested block.", + "in": "query", + "required": false, + "type": "string", + "format": "byte" + } + ], + "tags": [ + "ChainKit" + ] + } + }, + "/v2/chainkit/blockhash": { + "get": { + "summary": "lncli: `chain getblockhash`\nGetBlockHash returns the hash of the block in the best blockchain\nat the given height.", + "operationId": "ChainKit_GetBlockHash", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/chainrpcGetBlockHashResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "block_height", + "description": "Block height of the target best chain block.", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + } + ], + "tags": [ + "ChainKit" + ] + } + } + }, + "definitions": { + "chainrpcGetBestBlockResponse": { + "type": "object", + "properties": { + "block_hash": { + "type": "string", + "format": "byte", + "description": "The hash of the best block." + }, + "block_height": { + "type": "integer", + "format": "int32", + "description": "The height of the best block." + } + } + }, + "chainrpcGetBlockHashResponse": { + "type": "object", + "properties": { + "block_hash": { + "type": "string", + "format": "byte", + "description": "The hash of the best block at the specified height." + } + } + }, + "chainrpcGetBlockResponse": { + "type": "object", + "properties": { + "raw_block": { + "type": "string", + "format": "byte", + "description": "The raw bytes of the requested block." + } + }, + "description": "TODO(ffranr): The neutrino GetBlock response includes many\nadditional helpful fields. Consider adding them here also." + }, + "protobufAny": { + "type": "object", + "properties": { + "type_url": { + "type": "string" + }, + "value": { + "type": "string", + "format": "byte" + } + } + }, + "rpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/protobufAny" + } + } + } + } + } +} diff --git a/lnrpc/chainrpc/chainkit.yaml b/lnrpc/chainrpc/chainkit.yaml new file mode 100644 index 000000000..815dfc5a7 --- /dev/null +++ b/lnrpc/chainrpc/chainkit.yaml @@ -0,0 +1,11 @@ +type: google.api.Service +config_version: 3 + +http: + rules: + - selector: chainrpc.ChainKit.GetBlock + get: "/v2/chainkit/block" + - selector: chainrpc.ChainKit.GetBestBlock + get: "/v2/chainkit/bestblock" + - selector: chainrpc.ChainKit.GetBlockHash + get: "/v2/chainkit/blockhash" \ No newline at end of file diff --git a/lnrpc/chainrpc/chainkit_grpc.pb.go b/lnrpc/chainrpc/chainkit_grpc.pb.go new file mode 100644 index 000000000..47585f7f3 --- /dev/null +++ b/lnrpc/chainrpc/chainkit_grpc.pb.go @@ -0,0 +1,189 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package chainrpc + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// ChainKitClient is the client API for ChainKit service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +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 getbestblock` + // GetBestBlock returns the block hash and current height from the valid + // most-work chain. + GetBestBlock(ctx context.Context, in *GetBestBlockRequest, opts ...grpc.CallOption) (*GetBestBlockResponse, error) + // lncli: `chain getblockhash` + // GetBlockHash returns the hash of the block in the best blockchain + // at the given height. + GetBlockHash(ctx context.Context, in *GetBlockHashRequest, opts ...grpc.CallOption) (*GetBlockHashResponse, error) +} + +type chainKitClient struct { + cc grpc.ClientConnInterface +} + +func NewChainKitClient(cc grpc.ClientConnInterface) ChainKitClient { + return &chainKitClient{cc} +} + +func (c *chainKitClient) GetBlock(ctx context.Context, in *GetBlockRequest, opts ...grpc.CallOption) (*GetBlockResponse, error) { + out := new(GetBlockResponse) + err := c.cc.Invoke(ctx, "/chainrpc.ChainKit/GetBlock", 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...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *chainKitClient) GetBlockHash(ctx context.Context, in *GetBlockHashRequest, opts ...grpc.CallOption) (*GetBlockHashResponse, error) { + out := new(GetBlockHashResponse) + err := c.cc.Invoke(ctx, "/chainrpc.ChainKit/GetBlockHash", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ChainKitServer is the server API for ChainKit service. +// All implementations must embed UnimplementedChainKitServer +// for forward compatibility +type ChainKitServer interface { + // lncli: `chain getblock` + // GetBlock returns a block given the corresponding block hash. + GetBlock(context.Context, *GetBlockRequest) (*GetBlockResponse, error) + // lncli: `chain getbestblock` + // GetBestBlock returns the block hash and current height from the valid + // most-work chain. + GetBestBlock(context.Context, *GetBestBlockRequest) (*GetBestBlockResponse, error) + // lncli: `chain getblockhash` + // GetBlockHash returns the hash of the block in the best blockchain + // at the given height. + GetBlockHash(context.Context, *GetBlockHashRequest) (*GetBlockHashResponse, error) + mustEmbedUnimplementedChainKitServer() +} + +// UnimplementedChainKitServer must be embedded to have forward compatible implementations. +type UnimplementedChainKitServer struct { +} + +func (UnimplementedChainKitServer) GetBlock(context.Context, *GetBlockRequest) (*GetBlockResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetBlock not implemented") +} +func (UnimplementedChainKitServer) GetBestBlock(context.Context, *GetBestBlockRequest) (*GetBestBlockResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetBestBlock not implemented") +} +func (UnimplementedChainKitServer) GetBlockHash(context.Context, *GetBlockHashRequest) (*GetBlockHashResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetBlockHash not implemented") +} +func (UnimplementedChainKitServer) mustEmbedUnimplementedChainKitServer() {} + +// UnsafeChainKitServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ChainKitServer will +// result in compilation errors. +type UnsafeChainKitServer interface { + mustEmbedUnimplementedChainKitServer() +} + +func RegisterChainKitServer(s grpc.ServiceRegistrar, srv ChainKitServer) { + s.RegisterService(&ChainKit_ServiceDesc, srv) +} + +func _ChainKit_GetBlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetBlockRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ChainKitServer).GetBlock(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/chainrpc.ChainKit/GetBlock", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ChainKitServer).GetBlock(ctx, req.(*GetBlockRequest)) + } + 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 { + return nil, err + } + if interceptor == nil { + return srv.(ChainKitServer).GetBestBlock(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/chainrpc.ChainKit/GetBestBlock", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ChainKitServer).GetBestBlock(ctx, req.(*GetBestBlockRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ChainKit_GetBlockHash_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetBlockHashRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ChainKitServer).GetBlockHash(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/chainrpc.ChainKit/GetBlockHash", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ChainKitServer).GetBlockHash(ctx, req.(*GetBlockHashRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// ChainKit_ServiceDesc is the grpc.ServiceDesc for ChainKit service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ChainKit_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "chainrpc.ChainKit", + HandlerType: (*ChainKitServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetBlock", + Handler: _ChainKit_GetBlock_Handler, + }, + { + MethodName: "GetBestBlock", + Handler: _ChainKit_GetBestBlock_Handler, + }, + { + MethodName: "GetBlockHash", + Handler: _ChainKit_GetBlockHash_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "chainrpc/chainkit.proto", +} diff --git a/lnrpc/chainrpc/config_active.go b/lnrpc/chainrpc/config_active.go index 636798c72..c84e1ba49 100644 --- a/lnrpc/chainrpc/config_active.go +++ b/lnrpc/chainrpc/config_active.go @@ -5,6 +5,7 @@ package chainrpc import ( "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/macaroons" ) @@ -32,4 +33,7 @@ type Config struct { // notifier RPC server. The job of the chain notifier RPC server is // simply to proxy valid requests to the active chain notifier instance. ChainNotifier chainntnfs.ChainNotifier + + // Chain provides access to the most up-to-date blockchain data. + Chain lnwallet.BlockChainIO } diff --git a/lnrpc/chainrpc/driver.go b/lnrpc/chainrpc/driver.go index c25ffbff4..89a6a116a 100644 --- a/lnrpc/chainrpc/driver.go +++ b/lnrpc/chainrpc/driver.go @@ -45,9 +45,14 @@ func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( case config.MacService != nil && config.NetworkDir == "": return nil, nil, fmt.Errorf("NetworkDir must be set to create " + "chainrpc") + case config.ChainNotifier == nil: return nil, nil, fmt.Errorf("ChainNotifier must be set to " + "create chainrpc") + + case config.Chain == nil: + return nil, nil, fmt.Errorf("field Chain must be set to " + + "create chainrpc") } return New(config) diff --git a/lntemp/rpc/harness_rpc.go b/lntemp/rpc/harness_rpc.go index ca566035e..855227c60 100644 --- a/lntemp/rpc/harness_rpc.go +++ b/lntemp/rpc/harness_rpc.go @@ -39,6 +39,7 @@ type HarnessRPC struct { WatchtowerClient wtclientrpc.WatchtowerClientClient State lnrpc.StateClient ChainClient chainrpc.ChainNotifierClient + ChainKit chainrpc.ChainKitClient Peer peersrpc.PeersClient // Name is the HarnessNode's name. @@ -68,6 +69,7 @@ func NewHarnessRPC(ctxt context.Context, t *testing.T, c *grpc.ClientConn, Signer: signrpc.NewSignerClient(c), State: lnrpc.NewStateClient(c), ChainClient: chainrpc.NewChainNotifierClient(c), + ChainKit: chainrpc.NewChainKitClient(c), Peer: peersrpc.NewPeersClient(c), Name: name, } diff --git a/subrpcserver_config.go b/subrpcserver_config.go index 8911d1eb3..2723efaea 100644 --- a/subrpcserver_config.go +++ b/subrpcserver_config.go @@ -217,6 +217,9 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config, subCfgValue.FieldByName("ChainNotifier").Set( reflect.ValueOf(cc.ChainNotifier), ) + subCfgValue.FieldByName("Chain").Set( + reflect.ValueOf(cc.ChainIO), + ) case *invoicesrpc.Config: subCfgValue := extractReflectValue(subCfg) From 1345b3c0aae89695a72a4978d1461f57d42c14d7 Mon Sep 17 00:00:00 2001 From: ffranr Date: Fri, 2 Dec 2022 13:40:19 +0000 Subject: [PATCH 4/6] itest: add chainkit test This test covers GetBlock, GetBestBlock, and GetBlockHash. --- lntest/harness_node.go | 4 ++ lntest/itest/list_on_test.go | 4 ++ lntest/itest/lnd_onchain_test.go | 68 ++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) diff --git a/lntest/harness_node.go b/lntest/harness_node.go index 3b1e8bebc..74bb544f1 100644 --- a/lntest/harness_node.go +++ b/lntest/harness_node.go @@ -367,6 +367,7 @@ type HarnessNode struct { WatchtowerClient wtclientrpc.WatchtowerClientClient StateClient lnrpc.StateClient ChainClient chainrpc.ChainNotifierClient + ChainKit chainrpc.ChainKitClient } // RPCClients wraps a list of RPC clients into a single struct for easier @@ -385,6 +386,7 @@ type RPCClients struct { WatchtowerClient wtclientrpc.WatchtowerClientClient State lnrpc.StateClient ChainClient chainrpc.ChainNotifierClient + ChainKit chainrpc.ChainKitClient } // Assert *HarnessNode implements the lnrpc.LightningClient interface. @@ -938,6 +940,7 @@ func (hn *HarnessNode) InitRPCClients(c *grpc.ClientConn) { Signer: signrpc.NewSignerClient(c), State: lnrpc.NewStateClient(c), ChainClient: chainrpc.NewChainNotifierClient(c), + ChainKit: chainrpc.NewChainKitClient(c), } } @@ -960,6 +963,7 @@ func (hn *HarnessNode) initLightningClient() error { hn.PeersClient = peersrpc.NewPeersClient(conn) hn.StateClient = lnrpc.NewStateClient(conn) hn.ChainClient = chainrpc.NewChainNotifierClient(conn) + hn.ChainKit = chainrpc.NewChainKitClient(conn) // Wait until the server is fully started. if err := hn.WaitUntilServerActive(); err != nil { diff --git a/lntest/itest/list_on_test.go b/lntest/itest/list_on_test.go index c8a2d0266..d08aab674 100644 --- a/lntest/itest/list_on_test.go +++ b/lntest/itest/list_on_test.go @@ -219,4 +219,8 @@ var allTestCasesTemp = []*lntemp.TestCase{ Name: "failing link", TestFunc: testFailingChannel, }, + { + Name: "chain kit", + TestFunc: testChainKit, + }, } diff --git a/lntest/itest/lnd_onchain_test.go b/lntest/itest/lnd_onchain_test.go index add35d8a0..31af68363 100644 --- a/lntest/itest/lnd_onchain_test.go +++ b/lntest/itest/lnd_onchain_test.go @@ -12,7 +12,9 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/chainrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" + "github.com/lightningnetwork/lnd/lntemp" "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwallet" @@ -20,6 +22,72 @@ import ( "github.com/stretchr/testify/require" ) +// testChainKit tests ChainKit RPC endpoints. +func testChainKit(ht *lntemp.HarnessTest) { + ctx := context.Background() + + // Test functions registered as test cases spin up separate nodes during + // execution. By calling sub-test functions as seen below we avoid the + // need to start separate nodes. + testChainKitGetBlock(ctx, ht) + testChainKitGetBlockHash(ctx, ht) +} + +// testChainKitGetBlock ensures that given a block hash, the RPC endpoint +// returns the correct target block. +func testChainKitGetBlock(ctx context.Context, ht *lntemp.HarnessTest) { + // Get best block hash. + bestBlockRes, err := ht.Alice.RPC.ChainKit.GetBestBlock( + ctx, &chainrpc.GetBestBlockRequest{}, + ) + require.NoError(ht, err) + var bestBlockHash chainhash.Hash + err = bestBlockHash.SetBytes(bestBlockRes.BlockHash) + require.NoError(ht, err) + + // Retrieve the best block by hash. + getBlockRes, err := ht.Alice.RPC.ChainKit.GetBlock( + ctx, &chainrpc.GetBlockRequest{ + BlockHash: bestBlockHash.CloneBytes(), + }, + ) + require.NoError(ht, err) + + // Deserialize the block which was retrieved by hash. + msgBlock := &wire.MsgBlock{} + blockReader := bytes.NewReader(getBlockRes.RawBlock) + err = msgBlock.Deserialize(blockReader) + require.NoError(ht, err) + + // Ensure best block hash is the same as retrieved block hash. + expected := bestBlockHash + actual := msgBlock.BlockHash() + require.Equal(ht, expected, actual) +} + +// testChainKitGetBlockHash ensures that given a block height, the RPC endpoint +// returns the correct target block hash. +func testChainKitGetBlockHash(ctx context.Context, ht *lntemp.HarnessTest) { + // Get best block hash. + bestBlockRes, err := ht.Alice.RPC.ChainKit.GetBestBlock( + ctx, &chainrpc.GetBestBlockRequest{}, + ) + require.NoError(ht, err) + + // Retrieve the block hash at best block height. + getBlockHashRes, err := ht.Alice.RPC.ChainKit.GetBlockHash( + ctx, &chainrpc.GetBlockHashRequest{ + BlockHeight: int64(bestBlockRes.BlockHeight), + }, + ) + require.NoError(ht, err) + + // Ensure best block hash is the same as retrieved block hash. + expected := bestBlockRes.BlockHash + actual := getBlockHashRes.BlockHash + require.Equal(ht, expected, actual) +} + // testCPFP ensures that the daemon can bump an unconfirmed transaction's fee // rate by broadcasting a Child-Pays-For-Parent (CPFP) transaction. // From bab526f655cc7a80bc785a236571d95cef4f0bb5 Mon Sep 17 00:00:00 2001 From: ffranr Date: Thu, 1 Dec 2022 17:30:00 +0000 Subject: [PATCH 5/6] cmd: add chain subcommand Chain subcommand includes the commands: getblock, getbestblock, and getblockhash. This commit removes conflicting neutrino cli commands. --- cmd/lncli/chainrpc_active.go | 219 ++++++++++++++++++++++++++++++++++ cmd/lncli/chainrpc_default.go | 11 ++ cmd/lncli/main.go | 1 + cmd/lncli/neutrino_active.go | 81 ------------- go.mod | 2 +- go.sum | 2 + 6 files changed, 234 insertions(+), 82 deletions(-) create mode 100644 cmd/lncli/chainrpc_active.go create mode 100644 cmd/lncli/chainrpc_default.go diff --git a/cmd/lncli/chainrpc_active.go b/cmd/lncli/chainrpc_active.go new file mode 100644 index 000000000..0d8b891ac --- /dev/null +++ b/cmd/lncli/chainrpc_active.go @@ -0,0 +1,219 @@ +//go:build chainrpc +// +build chainrpc + +package main + +import ( + "bytes" + "fmt" + "strconv" + + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/lnrpc/chainrpc" + "github.com/urfave/cli" +) + +// chainCommands will return the set of commands to enable for chainrpc builds. +func chainCommands() []cli.Command { + return []cli.Command{ + { + Name: "chain", + Category: "On-chain", + Usage: "Interact with the bitcoin blockchain.", + Subcommands: []cli.Command{ + getBlockCommand, + getBestBlockCommand, + getBlockHashCommand, + }, + }, + } +} + +func getChainClient(ctx *cli.Context) (chainrpc.ChainKitClient, func()) { + conn := getClientConn(ctx, false) + + cleanUp := func() { + conn.Close() + } + + return chainrpc.NewChainKitClient(conn), cleanUp +} + +var getBlockCommand = cli.Command{ + Name: "getblock", + Category: "On-chain", + Usage: "Get block by block hash.", + Description: "Returns a block given the corresponding block hash.", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "hash", + Usage: "the target block hash", + }, + cli.BoolFlag{ + Name: "verbose", + Usage: "print entire block as JSON", + }, + }, + Action: actionDecorator(getBlock), +} + +func getBlock(ctx *cli.Context) error { + ctxc := getContext() + + var ( + args = ctx.Args() + blockHashString string + ) + + verbose := false + if ctx.IsSet("verbose") { + verbose = true + } + + switch { + case ctx.IsSet("hash"): + blockHashString = ctx.String("hash") + + case args.Present(): + blockHashString = args.First() + + default: + return fmt.Errorf("hash argument missing") + } + + blockHash, err := chainhash.NewHashFromStr(blockHashString) + if err != nil { + return err + } + + client, cleanUp := getChainClient(ctx) + defer cleanUp() + + req := &chainrpc.GetBlockRequest{BlockHash: blockHash.CloneBytes()} + resp, err := client.GetBlock(ctxc, req) + if err != nil { + return err + } + + // Convert raw block bytes into wire.MsgBlock. + msgBlock := &wire.MsgBlock{} + blockReader := bytes.NewReader(resp.RawBlock) + err = msgBlock.Deserialize(blockReader) + if err != nil { + return err + } + + if verbose { + printJSON(msgBlock) + } else { + printJSON(msgBlock.Header) + } + + return nil +} + +var getBestBlockCommand = cli.Command{ + Name: "getbestblock", + Category: "On-chain", + Usage: "Get best block.", + Description: "Returns the latest block hash and height from the " + + "valid most-work chain.", + Action: actionDecorator(getBestBlock), +} + +func getBestBlock(ctx *cli.Context) error { + ctxc := getContext() + + client, cleanUp := getChainClient(ctx) + defer cleanUp() + + resp, err := client.GetBestBlock(ctxc, &chainrpc.GetBestBlockRequest{}) + if err != nil { + return err + } + + // Cast gRPC block hash bytes as chain hash type. + var blockHash chainhash.Hash + copy(blockHash[:], resp.BlockHash) + + printJSON(struct { + BlockHash chainhash.Hash `json:"block_hash"` + BlockHeight int32 `json:"block_height"` + }{ + BlockHash: blockHash, + BlockHeight: resp.BlockHeight, + }) + + return nil +} + +var getBlockHashCommand = cli.Command{ + Name: "getblockhash", + Category: "On-chain", + Usage: "Get block hash by block height.", + Description: "Returns the block hash from the best chain at a given " + + "height.", + Flags: []cli.Flag{ + cli.Int64Flag{ + Name: "height", + Usage: "target block height", + }, + }, + Action: actionDecorator(getBlockHash), +} + +func getBlockHash(ctx *cli.Context) error { + ctxc := getContext() + + // Display the command's help message if we do not have the expected + // number of arguments/flags. + if ctx.NArg()+ctx.NumFlags() != 1 { + return cli.ShowCommandHelp(ctx, "getblockhash") + } + + var ( + args = ctx.Args() + blockHeight int64 + ) + + switch { + case ctx.IsSet("height"): + blockHeight = ctx.Int64("height") + + case args.Present(): + blockHeightString := args.First() + + // Convert block height positional argument from string to + // int64. + var err error + blockHeight, err = strconv.ParseInt(blockHeightString, 10, 64) + if err != nil { + return err + } + + default: + return fmt.Errorf("block height argument missing") + } + + client, cleanUp := getChainClient(ctx) + defer cleanUp() + + req := &chainrpc.GetBlockHashRequest{BlockHeight: blockHeight} + resp, err := client.GetBlockHash(ctxc, req) + if err != nil { + return err + } + + // Cast gRPC block hash bytes as chain hash type. + var blockHash chainhash.Hash + copy(blockHash[:], resp.BlockHash) + + printJSON(struct { + BlockHash chainhash.Hash `json:"block_hash"` + }{ + BlockHash: blockHash, + }) + + return nil +} diff --git a/cmd/lncli/chainrpc_default.go b/cmd/lncli/chainrpc_default.go new file mode 100644 index 000000000..fa1ea99e2 --- /dev/null +++ b/cmd/lncli/chainrpc_default.go @@ -0,0 +1,11 @@ +//go:build !chainrpc +// +build !chainrpc + +package main + +import "github.com/urfave/cli" + +// chainCommands will return nil for non-chainrpc builds. +func chainCommands() []cli.Command { + return nil +} diff --git a/cmd/lncli/main.go b/cmd/lncli/main.go index 116a42b13..4783ec06d 100644 --- a/cmd/lncli/main.go +++ b/cmd/lncli/main.go @@ -500,6 +500,7 @@ func main() { app.Commands = append(app.Commands, wtclientCommands()...) app.Commands = append(app.Commands, devCommands()...) app.Commands = append(app.Commands, peersCommands()...) + app.Commands = append(app.Commands, chainCommands()...) if err := app.Run(os.Args); err != nil { fatal(err) diff --git a/cmd/lncli/neutrino_active.go b/cmd/lncli/neutrino_active.go index 093821a3d..4267738e1 100644 --- a/cmd/lncli/neutrino_active.go +++ b/cmd/lncli/neutrino_active.go @@ -4,8 +4,6 @@ package main import ( - "strconv" - "github.com/lightningnetwork/lnd/lnrpc/neutrinorpc" "github.com/urfave/cli" ) @@ -193,42 +191,6 @@ func getBlockHeader(ctx *cli.Context) error { return nil } -var getBlockCommand = cli.Command{ - Name: "getblock", - Usage: "Get a block.", - Category: "Neutrino", - Description: "Returns a block with a particular block hash.", - ArgsUsage: "hash", - Action: actionDecorator(getBlock), -} - -func getBlock(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, "getblock") - } - - client, cleanUp := getNeutrinoKitClient(ctx) - defer cleanUp() - - req := &neutrinorpc.GetBlockRequest{ - Hash: args.First(), - } - - resp, err := client.GetBlock(ctxc, req) - if err != nil { - return err - } - - printRespJSON(resp) - - return nil -} - var getCFilterCommand = cli.Command{ Name: "getcfilter", Usage: "Get a compact filter.", @@ -263,47 +225,6 @@ func getCFilter(ctx *cli.Context) error { return nil } -var getBlockHashCommand = cli.Command{ - Name: "getblockhash", - Usage: "Get a block hash.", - Category: "Neutrino", - Description: "Returns the header hash of a block at a given height.", - ArgsUsage: "height", - Action: actionDecorator(getBlockHash), -} - -func getBlockHash(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, "getblockhash") - } - - client, cleanUp := getNeutrinoKitClient(ctx) - defer cleanUp() - - height, err := strconv.ParseInt(args.First(), 10, 32) - if err != nil { - return err - } - - req := &neutrinorpc.GetBlockHashRequest{ - Height: int32(height), - } - - resp, err := client.GetBlockHash(ctxc, req) - if err != nil { - return err - } - - printRespJSON(resp) - - return nil -} - // neutrinoCommands will return the set of commands to enable for neutrinorpc // builds. func neutrinoCommands() []cli.Command { @@ -318,10 +239,8 @@ func neutrinoCommands() []cli.Command { addPeerCommand, disconnectPeerCommand, isBannedCommand, - getBlockCommand, getBlockHeaderCommand, getCFilterCommand, - getBlockHashCommand, }, }, } diff --git a/go.mod b/go.mod index 689eb764b..048f0de24 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.2.2 github.com/btcsuite/btcd/btcutil v1.1.3 github.com/btcsuite/btcd/btcutil/psbt v1.1.5 - github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 + github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f github.com/btcsuite/btcwallet v0.16.6-0.20221203002441-6c7480c8a46b github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2 diff --git a/go.sum b/go.sum index 023e5fd69..1c4856482 100644 --- a/go.sum +++ b/go.sum @@ -96,6 +96,8 @@ github.com/btcsuite/btcd/btcutil/psbt v1.1.5/go.mod h1:kA6FLH/JfUx++j9pYU0pyu+Z8 github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= From d72d2edfeaec35d863d840735e3a6bbe94238c46 Mon Sep 17 00:00:00 2001 From: ffranr Date: Thu, 1 Dec 2022 23:35:13 +0000 Subject: [PATCH 6/6] docs: add release notes for chainkit RPC/cli --- docs/release-notes/release-notes-0.16.0.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/release-notes/release-notes-0.16.0.md b/docs/release-notes/release-notes-0.16.0.md index e6df3304e..4afff6d7b 100644 --- a/docs/release-notes/release-notes-0.16.0.md +++ b/docs/release-notes/release-notes-0.16.0.md @@ -73,6 +73,11 @@ `ListInvoiceRequest` and `ListPaymentsRequest`](https://github.com/lightningnetwork/lnd/pull/7159). +* [Add chainkit RPC endpoints](https://github.com/lightningnetwork/lnd/pull/7197): + GetBlock, GetBestBlock, GetBlockHash. These endpoints provide access to chain + block data. + + ## Wallet * [Allows Taproot public keys and tap scripts to be imported as watch-only @@ -205,6 +210,11 @@ certain large transactions](https://github.com/lightningnetwork/lnd/pull/7100). * [Allow lncli to read binary PSBTs](https://github.com/lightningnetwork/lnd/pull/7122) from a file during PSBT channel funding flow to comply with [BIP 174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#specification) +* [Add interface to chainkit RPC](https://github.com/lightningnetwork/lnd/pull/7197). + This addition consists of the `chain` subcommand group: `getblock`, + `getblockhash`, and `getbestblock`. These commands provide access to chain + block data. + ## Code Health * [test: use `T.TempDir` to create temporary test @@ -300,6 +310,7 @@ refactor the itest for code health and maintenance. * Olaoluwa Osuntokun * Oliver Gugger * Priyansh Rastogi +* Robyn Ffrancon * Roei Erez * Tommy Volk * Yong Yu