Merge pull request #2081 from Roasbeef/signer-service
lnrpc+lnd+rpc: add new Signer RPC service, and sub RPC server infrastructure
This commit is contained in:
commit
fc4fe07010
@ -24,6 +24,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/build"
|
"github.com/lightningnetwork/lnd/build"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hodl"
|
"github.com/lightningnetwork/lnd/htlcswitch/hodl"
|
||||||
"github.com/lightningnetwork/lnd/lncfg"
|
"github.com/lightningnetwork/lnd/lncfg"
|
||||||
|
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing"
|
"github.com/lightningnetwork/lnd/routing"
|
||||||
"github.com/lightningnetwork/lnd/tor"
|
"github.com/lightningnetwork/lnd/tor"
|
||||||
@ -220,6 +221,8 @@ type config struct {
|
|||||||
|
|
||||||
Tor *torConfig `group:"Tor" namespace:"tor"`
|
Tor *torConfig `group:"Tor" namespace:"tor"`
|
||||||
|
|
||||||
|
SubRPCServers *subRPCServerConfigs `group:"subrpc"`
|
||||||
|
|
||||||
Hodl *hodl.Config `group:"hodl" namespace:"hodl"`
|
Hodl *hodl.Config `group:"hodl" namespace:"hodl"`
|
||||||
|
|
||||||
NoNetBootstrap bool `long:"nobootstrap" description:"If true, then automatic network bootstrapping will not be attempted."`
|
NoNetBootstrap bool `long:"nobootstrap" description:"If true, then automatic network bootstrapping will not be attempted."`
|
||||||
@ -295,6 +298,9 @@ func loadConfig() (*config, error) {
|
|||||||
},
|
},
|
||||||
MaxPendingChannels: defaultMaxPendingChannels,
|
MaxPendingChannels: defaultMaxPendingChannels,
|
||||||
NoSeedBackup: defaultNoSeedBackup,
|
NoSeedBackup: defaultNoSeedBackup,
|
||||||
|
SubRPCServers: &subRPCServerConfigs{
|
||||||
|
SignRPC: &signrpc.Config{},
|
||||||
|
},
|
||||||
Autopilot: &autoPilotConfig{
|
Autopilot: &autoPilotConfig{
|
||||||
MaxChannels: 5,
|
MaxChannels: 5,
|
||||||
Allocation: 0.6,
|
Allocation: 0.6,
|
||||||
|
68
lnd.go
68
lnd.go
@ -312,67 +312,21 @@ func lndMain() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check macaroon authentication if macaroons aren't disabled.
|
|
||||||
if macaroonService != nil {
|
|
||||||
serverOpts = append(serverOpts,
|
|
||||||
grpc.UnaryInterceptor(macaroonService.
|
|
||||||
UnaryServerInterceptor(permissions)),
|
|
||||||
grpc.StreamInterceptor(macaroonService.
|
|
||||||
StreamServerInterceptor(permissions)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize, and register our implementation of the gRPC interface
|
// Initialize, and register our implementation of the gRPC interface
|
||||||
// exported by the rpcServer.
|
// exported by the rpcServer.
|
||||||
rpcServer := newRPCServer(server)
|
rpcServer, err := newRPCServer(
|
||||||
|
server, macaroonService, cfg.SubRPCServers, serverOpts,
|
||||||
|
proxyOpts, tlsConf,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
srvrLog.Errorf("unable to start RPC server: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := rpcServer.Start(); err != nil {
|
if err := rpcServer.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer rpcServer.Stop()
|
defer rpcServer.Stop()
|
||||||
|
|
||||||
grpcServer := grpc.NewServer(serverOpts...)
|
|
||||||
lnrpc.RegisterLightningServer(grpcServer, rpcServer)
|
|
||||||
|
|
||||||
// Next, Start the gRPC server listening for HTTP/2 connections.
|
|
||||||
for _, listener := range cfg.RPCListeners {
|
|
||||||
lis, err := lncfg.ListenOnAddress(listener)
|
|
||||||
if err != nil {
|
|
||||||
ltndLog.Errorf(
|
|
||||||
"RPC server unable to listen on %s", listener,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer lis.Close()
|
|
||||||
go func() {
|
|
||||||
rpcsLog.Infof("RPC server listening on %s", lis.Addr())
|
|
||||||
grpcServer.Serve(lis)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, start the REST proxy for our gRPC server above.
|
|
||||||
mux := proxy.NewServeMux()
|
|
||||||
err = lnrpc.RegisterLightningHandlerFromEndpoint(
|
|
||||||
ctx, mux, cfg.RPCListeners[0].String(), proxyOpts,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, restEndpoint := range cfg.RESTListeners {
|
|
||||||
lis, err := lncfg.TLSListenOnAddress(restEndpoint, tlsConf)
|
|
||||||
if err != nil {
|
|
||||||
ltndLog.Errorf(
|
|
||||||
"gRPC proxy unable to listen on %s",
|
|
||||||
restEndpoint,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer lis.Close()
|
|
||||||
go func() {
|
|
||||||
rpcsLog.Infof("gRPC proxy started at %s", lis.Addr())
|
|
||||||
http.Serve(lis, mux)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're not in simnet mode, We'll wait until we're fully synced to
|
// If we're not in simnet mode, We'll wait until we're fully synced to
|
||||||
// continue the start up of the remainder of the daemon. This ensures
|
// continue the start up of the remainder of the daemon. This ensures
|
||||||
// that we don't accept any possibly invalid state transitions, or
|
// that we don't accept any possibly invalid state transitions, or
|
||||||
@ -602,9 +556,9 @@ func genCertPair(certFile, keyFile string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// genMacaroons generates three macaroon files; one admin-level, one
|
// genMacaroons generates three macaroon files; one admin-level, one for
|
||||||
// for invoice access and one read-only. These can also be used
|
// invoice access and one read-only. These can also be used to generate more
|
||||||
// to generate more granular macaroons.
|
// granular macaroons.
|
||||||
func genMacaroons(ctx context.Context, svc *macaroons.Service,
|
func genMacaroons(ctx context.Context, svc *macaroons.Service,
|
||||||
admFile, roFile, invoiceFile string) error {
|
admFile, roFile, invoiceFile string) error {
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Generating root gRPC server protos"
|
||||||
|
|
||||||
# Generate the protos.
|
# Generate the protos.
|
||||||
protoc -I/usr/local/include -I. \
|
protoc -I/usr/local/include -I. \
|
||||||
-I$GOPATH/src \
|
-I$GOPATH/src \
|
||||||
@ -7,8 +9,6 @@ protoc -I/usr/local/include -I. \
|
|||||||
--go_out=plugins=grpc:. \
|
--go_out=plugins=grpc:. \
|
||||||
rpc.proto
|
rpc.proto
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Generate the REST reverse proxy.
|
# Generate the REST reverse proxy.
|
||||||
protoc -I/usr/local/include -I. \
|
protoc -I/usr/local/include -I. \
|
||||||
-I$GOPATH/src \
|
-I$GOPATH/src \
|
||||||
@ -22,3 +22,16 @@ protoc -I/usr/local/include -I. \
|
|||||||
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
|
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
|
||||||
--swagger_out=logtostderr=true:. \
|
--swagger_out=logtostderr=true:. \
|
||||||
rpc.proto
|
rpc.proto
|
||||||
|
|
||||||
|
# For each of the sub-servers, we then generate their protos, but a restricted
|
||||||
|
# set as they don't yet require REST proxies, or swagger docs.
|
||||||
|
for file in **/*.proto
|
||||||
|
do
|
||||||
|
DIRECTORY=$(dirname ${file})
|
||||||
|
echo "Generating protos from ${file}, into ${DIRECTORY}"
|
||||||
|
|
||||||
|
protoc -I/usr/local/include -I. \
|
||||||
|
-I$GOPATH/src \
|
||||||
|
--go_out=plugins=grpc:. \
|
||||||
|
${file}
|
||||||
|
done
|
||||||
|
@ -522,7 +522,9 @@ func (m *FeeLimit) String() string { return proto.CompactTextString(m
|
|||||||
func (*FeeLimit) ProtoMessage() {}
|
func (*FeeLimit) ProtoMessage() {}
|
||||||
func (*FeeLimit) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
|
func (*FeeLimit) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
|
||||||
|
|
||||||
type isFeeLimit_Limit interface{ isFeeLimit_Limit() }
|
type isFeeLimit_Limit interface {
|
||||||
|
isFeeLimit_Limit()
|
||||||
|
}
|
||||||
|
|
||||||
type FeeLimit_Fixed struct {
|
type FeeLimit_Fixed struct {
|
||||||
Fixed int64 `protobuf:"varint,1,opt,name=fixed,oneof"`
|
Fixed int64 `protobuf:"varint,1,opt,name=fixed,oneof"`
|
||||||
@ -789,7 +791,9 @@ func (m *ChannelPoint) String() string { return proto.CompactTextStri
|
|||||||
func (*ChannelPoint) ProtoMessage() {}
|
func (*ChannelPoint) ProtoMessage() {}
|
||||||
func (*ChannelPoint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} }
|
func (*ChannelPoint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} }
|
||||||
|
|
||||||
type isChannelPoint_FundingTxid interface{ isChannelPoint_FundingTxid() }
|
type isChannelPoint_FundingTxid interface {
|
||||||
|
isChannelPoint_FundingTxid()
|
||||||
|
}
|
||||||
|
|
||||||
type ChannelPoint_FundingTxidBytes struct {
|
type ChannelPoint_FundingTxidBytes struct {
|
||||||
FundingTxidBytes []byte `protobuf:"bytes,1,opt,name=funding_txid_bytes,proto3,oneof"`
|
FundingTxidBytes []byte `protobuf:"bytes,1,opt,name=funding_txid_bytes,proto3,oneof"`
|
||||||
@ -2046,7 +2050,9 @@ func (m *CloseStatusUpdate) String() string { return proto.CompactTex
|
|||||||
func (*CloseStatusUpdate) ProtoMessage() {}
|
func (*CloseStatusUpdate) ProtoMessage() {}
|
||||||
func (*CloseStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{47} }
|
func (*CloseStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{47} }
|
||||||
|
|
||||||
type isCloseStatusUpdate_Update interface{ isCloseStatusUpdate_Update() }
|
type isCloseStatusUpdate_Update interface {
|
||||||
|
isCloseStatusUpdate_Update()
|
||||||
|
}
|
||||||
|
|
||||||
type CloseStatusUpdate_ClosePending struct {
|
type CloseStatusUpdate_ClosePending struct {
|
||||||
ClosePending *PendingUpdate `protobuf:"bytes,1,opt,name=close_pending,oneof"`
|
ClosePending *PendingUpdate `protobuf:"bytes,1,opt,name=close_pending,oneof"`
|
||||||
@ -2327,7 +2333,9 @@ func (m *OpenStatusUpdate) String() string { return proto.CompactText
|
|||||||
func (*OpenStatusUpdate) ProtoMessage() {}
|
func (*OpenStatusUpdate) ProtoMessage() {}
|
||||||
func (*OpenStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{50} }
|
func (*OpenStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{50} }
|
||||||
|
|
||||||
type isOpenStatusUpdate_Update interface{ isOpenStatusUpdate_Update() }
|
type isOpenStatusUpdate_Update interface {
|
||||||
|
isOpenStatusUpdate_Update()
|
||||||
|
}
|
||||||
|
|
||||||
type OpenStatusUpdate_ChanPending struct {
|
type OpenStatusUpdate_ChanPending struct {
|
||||||
ChanPending *PendingUpdate `protobuf:"bytes,1,opt,name=chan_pending,oneof"`
|
ChanPending *PendingUpdate `protobuf:"bytes,1,opt,name=chan_pending,oneof"`
|
||||||
@ -4668,7 +4676,9 @@ func (m *PolicyUpdateRequest) String() string { return proto.CompactT
|
|||||||
func (*PolicyUpdateRequest) ProtoMessage() {}
|
func (*PolicyUpdateRequest) ProtoMessage() {}
|
||||||
func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{102} }
|
func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{102} }
|
||||||
|
|
||||||
type isPolicyUpdateRequest_Scope interface{ isPolicyUpdateRequest_Scope() }
|
type isPolicyUpdateRequest_Scope interface {
|
||||||
|
isPolicyUpdateRequest_Scope()
|
||||||
|
}
|
||||||
|
|
||||||
type PolicyUpdateRequest_Global struct {
|
type PolicyUpdateRequest_Global struct {
|
||||||
Global bool `protobuf:"varint,1,opt,name=global,oneof"`
|
Global bool `protobuf:"varint,1,opt,name=global,oneof"`
|
||||||
|
33
lnrpc/signrpc/config_active.go
Normal file
33
lnrpc/signrpc/config_active.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// +build signrpc
|
||||||
|
|
||||||
|
package signrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
|
"github.com/lightningnetwork/lnd/macaroons"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is the primary configuration struct for the signer RPC server. It
|
||||||
|
// contains all the items required for the signer rpc server to carry out its
|
||||||
|
// duties. The fields with struct tags are meant to be parsed as normal
|
||||||
|
// configuration options, while if able to be populated, the latter fields MUST
|
||||||
|
// also be specified.
|
||||||
|
type Config struct {
|
||||||
|
// SignerMacPath is the path for the signer macaroon. If unspecified
|
||||||
|
// then we assume that the macaroon will be found under the network
|
||||||
|
// directory, named DefaultSignerMacFilename.
|
||||||
|
SignerMacPath string `long:"signermacaroonpath" description:"Path to the signer macaroon"`
|
||||||
|
|
||||||
|
// NetworkDir is the main network directory wherein the signer rpc
|
||||||
|
// server will find the macaroon named DefaultSignerMacFilename.
|
||||||
|
NetworkDir string
|
||||||
|
|
||||||
|
// MacService is the main macaroon service that we'll use to handle
|
||||||
|
// authentication for the signer rpc server.
|
||||||
|
MacService *macaroons.Service
|
||||||
|
|
||||||
|
// Signer is the signer instance that backs the signer RPC server. The
|
||||||
|
// job of the signer RPC server is simply to proxy valid requests to
|
||||||
|
// the active signer instance.
|
||||||
|
Signer lnwallet.Signer
|
||||||
|
}
|
6
lnrpc/signrpc/config_default.go
Normal file
6
lnrpc/signrpc/config_default.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// +build !signrpc
|
||||||
|
|
||||||
|
package signrpc
|
||||||
|
|
||||||
|
// Config is empty for non-signrpc builds.
|
||||||
|
type Config struct{}
|
71
lnrpc/signrpc/driver.go
Normal file
71
lnrpc/signrpc/driver.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// +build signrpc
|
||||||
|
|
||||||
|
package signrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// createNewSubServer is a helper method that will create the new signer sub
|
||||||
|
// server given the main config dispatcher method. If we're unable to find the
|
||||||
|
// config that is meant for us in the config dispatcher, then we'll exit with
|
||||||
|
// an error.
|
||||||
|
func createNewSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
|
||||||
|
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||||
|
|
||||||
|
// We'll attempt to look up the config that we expect, according to our
|
||||||
|
// subServerName name. If we can't find this, then we'll exit with an
|
||||||
|
// error, as we're unable to properly initialize ourselves without this
|
||||||
|
// config.
|
||||||
|
signServerConf, ok := configRegistry.FetchConfig(subServerName)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, fmt.Errorf("unable to find config for "+
|
||||||
|
"subserver type %s", subServerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we've found an object mapping to our service name, we'll
|
||||||
|
// ensure that it's the type we need.
|
||||||
|
config, ok := signServerConf.(*Config)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, fmt.Errorf("wrong type of config for "+
|
||||||
|
"subserver %s, expected %T got %T", subServerName,
|
||||||
|
&Config{}, signServerConf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before we try to make the new signer service instance, we'll perform
|
||||||
|
// some sanity checks on the arguments to ensure that they're useable.
|
||||||
|
|
||||||
|
switch {
|
||||||
|
// If the macaroon service is set (we should use macaroons), then
|
||||||
|
// ensure that we know where to look for them, or create them if not
|
||||||
|
// found.
|
||||||
|
case config.MacService != nil && config.NetworkDir == "":
|
||||||
|
return nil, nil, fmt.Errorf("NetworkDir must be set to create " +
|
||||||
|
"Signrpc")
|
||||||
|
case config.Signer == nil:
|
||||||
|
return nil, nil, fmt.Errorf("Signer must be set to create " +
|
||||||
|
"Signrpc")
|
||||||
|
}
|
||||||
|
|
||||||
|
return New(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
subServer := &lnrpc.SubServerDriver{
|
||||||
|
SubServerName: subServerName,
|
||||||
|
New: func(c lnrpc.SubServerConfigDispatcher) (
|
||||||
|
lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
|
||||||
|
|
||||||
|
return createNewSubServer(c)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the build tag is active, then we'll register ourselves as a
|
||||||
|
// sub-RPC server within the global lnrpc package namespace.
|
||||||
|
if err := lnrpc.RegisterSubServer(subServer); err != nil {
|
||||||
|
panic(fmt.Sprintf("failed to register sub server driver '%s': %v",
|
||||||
|
subServerName, err))
|
||||||
|
}
|
||||||
|
}
|
45
lnrpc/signrpc/log.go
Normal file
45
lnrpc/signrpc/log.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package signrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/btcsuite/btclog"
|
||||||
|
"github.com/lightningnetwork/lnd/build"
|
||||||
|
)
|
||||||
|
|
||||||
|
// log is a logger that is initialized with no output filters. This
|
||||||
|
// means the package will not perform any logging by default until the caller
|
||||||
|
// requests it.
|
||||||
|
var log btclog.Logger
|
||||||
|
|
||||||
|
// The default amount of logging is none.
|
||||||
|
func init() {
|
||||||
|
UseLogger(build.NewSubLogger("SGNR", nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableLog disables all library log output. Logging output is disabled
|
||||||
|
// by default until UseLogger is called.
|
||||||
|
func DisableLog() {
|
||||||
|
UseLogger(btclog.Disabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseLogger uses a specified Logger to output package logging info.
|
||||||
|
// This should be used in preference to SetLogWriter if the caller is also
|
||||||
|
// using btclog.
|
||||||
|
func UseLogger(logger btclog.Logger) {
|
||||||
|
log = logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// logClosure is used to provide a closure over expensive logging operations so
|
||||||
|
// don't have to be performed when the logging level doesn't warrant it.
|
||||||
|
type logClosure func() string
|
||||||
|
|
||||||
|
// String invokes the underlying function and returns the result.
|
||||||
|
func (c logClosure) String() string {
|
||||||
|
return c()
|
||||||
|
}
|
||||||
|
|
||||||
|
// newLogClosure returns a new closure over a function that returns a string
|
||||||
|
// which itself provides a Stringer interface so that it can be used with the
|
||||||
|
// logging system.
|
||||||
|
func newLogClosure(c func() string) logClosure {
|
||||||
|
return logClosure(c)
|
||||||
|
}
|
588
lnrpc/signrpc/signer.pb.go
Normal file
588
lnrpc/signrpc/signer.pb.go
Normal file
@ -0,0 +1,588 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: signrpc/signer.proto
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package signrpc is a generated protocol buffer package.
|
||||||
|
|
||||||
|
It is generated from these files:
|
||||||
|
signrpc/signer.proto
|
||||||
|
|
||||||
|
It has these top-level messages:
|
||||||
|
KeyLocator
|
||||||
|
KeyDescriptor
|
||||||
|
TxOut
|
||||||
|
SignDescriptor
|
||||||
|
SignReq
|
||||||
|
SignResp
|
||||||
|
InputScript
|
||||||
|
InputScriptResp
|
||||||
|
*/
|
||||||
|
package signrpc
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "golang.org/x/net/context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
type KeyLocator struct {
|
||||||
|
// / The family of key being identified.
|
||||||
|
KeyFamily int32 `protobuf:"varint,1,opt,name=key_family,json=keyFamily" json:"key_family,omitempty"`
|
||||||
|
// / The precise index of the key being identified.
|
||||||
|
KeyIndex int32 `protobuf:"varint,2,opt,name=key_index,json=keyIndex" json:"key_index,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *KeyLocator) Reset() { *m = KeyLocator{} }
|
||||||
|
func (m *KeyLocator) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*KeyLocator) ProtoMessage() {}
|
||||||
|
func (*KeyLocator) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||||
|
|
||||||
|
func (m *KeyLocator) GetKeyFamily() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.KeyFamily
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *KeyLocator) GetKeyIndex() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.KeyIndex
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyDescriptor struct {
|
||||||
|
// Types that are valid to be assigned to Key:
|
||||||
|
// *KeyDescriptor_RawKeyBytes
|
||||||
|
// *KeyDescriptor_KeyLoc
|
||||||
|
Key isKeyDescriptor_Key `protobuf_oneof:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *KeyDescriptor) Reset() { *m = KeyDescriptor{} }
|
||||||
|
func (m *KeyDescriptor) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*KeyDescriptor) ProtoMessage() {}
|
||||||
|
func (*KeyDescriptor) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||||
|
|
||||||
|
type isKeyDescriptor_Key interface {
|
||||||
|
isKeyDescriptor_Key()
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyDescriptor_RawKeyBytes struct {
|
||||||
|
RawKeyBytes []byte `protobuf:"bytes,1,opt,name=raw_key_bytes,json=rawKeyBytes,proto3,oneof"`
|
||||||
|
}
|
||||||
|
type KeyDescriptor_KeyLoc struct {
|
||||||
|
KeyLoc *KeyLocator `protobuf:"bytes,2,opt,name=key_loc,json=keyLoc,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*KeyDescriptor_RawKeyBytes) isKeyDescriptor_Key() {}
|
||||||
|
func (*KeyDescriptor_KeyLoc) isKeyDescriptor_Key() {}
|
||||||
|
|
||||||
|
func (m *KeyDescriptor) GetKey() isKeyDescriptor_Key {
|
||||||
|
if m != nil {
|
||||||
|
return m.Key
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *KeyDescriptor) GetRawKeyBytes() []byte {
|
||||||
|
if x, ok := m.GetKey().(*KeyDescriptor_RawKeyBytes); ok {
|
||||||
|
return x.RawKeyBytes
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *KeyDescriptor) GetKeyLoc() *KeyLocator {
|
||||||
|
if x, ok := m.GetKey().(*KeyDescriptor_KeyLoc); ok {
|
||||||
|
return x.KeyLoc
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX_OneofFuncs is for the internal use of the proto package.
|
||||||
|
func (*KeyDescriptor) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
|
||||||
|
return _KeyDescriptor_OneofMarshaler, _KeyDescriptor_OneofUnmarshaler, _KeyDescriptor_OneofSizer, []interface{}{
|
||||||
|
(*KeyDescriptor_RawKeyBytes)(nil),
|
||||||
|
(*KeyDescriptor_KeyLoc)(nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _KeyDescriptor_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
|
||||||
|
m := msg.(*KeyDescriptor)
|
||||||
|
// key
|
||||||
|
switch x := m.Key.(type) {
|
||||||
|
case *KeyDescriptor_RawKeyBytes:
|
||||||
|
b.EncodeVarint(1<<3 | proto.WireBytes)
|
||||||
|
b.EncodeRawBytes(x.RawKeyBytes)
|
||||||
|
case *KeyDescriptor_KeyLoc:
|
||||||
|
b.EncodeVarint(2<<3 | proto.WireBytes)
|
||||||
|
if err := b.EncodeMessage(x.KeyLoc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("KeyDescriptor.Key has unexpected type %T", x)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _KeyDescriptor_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
|
||||||
|
m := msg.(*KeyDescriptor)
|
||||||
|
switch tag {
|
||||||
|
case 1: // key.raw_key_bytes
|
||||||
|
if wire != proto.WireBytes {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
x, err := b.DecodeRawBytes(true)
|
||||||
|
m.Key = &KeyDescriptor_RawKeyBytes{x}
|
||||||
|
return true, err
|
||||||
|
case 2: // key.key_loc
|
||||||
|
if wire != proto.WireBytes {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
msg := new(KeyLocator)
|
||||||
|
err := b.DecodeMessage(msg)
|
||||||
|
m.Key = &KeyDescriptor_KeyLoc{msg}
|
||||||
|
return true, err
|
||||||
|
default:
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _KeyDescriptor_OneofSizer(msg proto.Message) (n int) {
|
||||||
|
m := msg.(*KeyDescriptor)
|
||||||
|
// key
|
||||||
|
switch x := m.Key.(type) {
|
||||||
|
case *KeyDescriptor_RawKeyBytes:
|
||||||
|
n += proto.SizeVarint(1<<3 | proto.WireBytes)
|
||||||
|
n += proto.SizeVarint(uint64(len(x.RawKeyBytes)))
|
||||||
|
n += len(x.RawKeyBytes)
|
||||||
|
case *KeyDescriptor_KeyLoc:
|
||||||
|
s := proto.Size(x.KeyLoc)
|
||||||
|
n += proto.SizeVarint(2<<3 | proto.WireBytes)
|
||||||
|
n += proto.SizeVarint(uint64(s))
|
||||||
|
n += s
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
type TxOut struct {
|
||||||
|
// / The value of the output being spent.
|
||||||
|
Value int64 `protobuf:"varint,1,opt,name=value" json:"value,omitempty"`
|
||||||
|
// / The script of the output being spent.
|
||||||
|
PkScript []byte `protobuf:"bytes,2,opt,name=pk_script,json=pkScript,proto3" json:"pk_script,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TxOut) Reset() { *m = TxOut{} }
|
||||||
|
func (m *TxOut) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*TxOut) ProtoMessage() {}
|
||||||
|
func (*TxOut) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||||
|
|
||||||
|
func (m *TxOut) GetValue() int64 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Value
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *TxOut) GetPkScript() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.PkScript
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignDescriptor struct {
|
||||||
|
// *
|
||||||
|
// A descriptor that precisely describes *which* key to use for signing. This
|
||||||
|
// may provide the raw public key directly, or require the Signer to re-derive
|
||||||
|
// the key according to the populated derivation path.
|
||||||
|
KeyDesc *KeyDescriptor `protobuf:"bytes,1,opt,name=key_desc,json=keyDesc" json:"key_desc,omitempty"`
|
||||||
|
// *
|
||||||
|
// A scalar value that will be added to the private key corresponding to the
|
||||||
|
// above public key to obtain the private key to be used to sign this input.
|
||||||
|
// This value is typically derived via the following computation:
|
||||||
|
//
|
||||||
|
// derivedKey = privkey + sha256(perCommitmentPoint || pubKey) mod N
|
||||||
|
SingleTweak []byte `protobuf:"bytes,2,opt,name=single_tweak,json=singleTweak,proto3" json:"single_tweak,omitempty"`
|
||||||
|
// *
|
||||||
|
// A private key that will be used in combination with its corresponding
|
||||||
|
// private key to derive the private key that is to be used to sign the target
|
||||||
|
// input. Within the Lightning protocol, this value is typically the
|
||||||
|
// commitment secret from a previously revoked commitment transaction. This
|
||||||
|
// value is in combination with two hash values, and the original private key
|
||||||
|
// to derive the private key to be used when signing.
|
||||||
|
//
|
||||||
|
// k = (privKey*sha256(pubKey || tweakPub) +
|
||||||
|
// tweakPriv*sha256(tweakPub || pubKey)) mod N
|
||||||
|
DoubleTweak []byte `protobuf:"bytes,3,opt,name=double_tweak,json=doubleTweak,proto3" json:"double_tweak,omitempty"`
|
||||||
|
// *
|
||||||
|
// The full script required to properly redeem the output. This field will
|
||||||
|
// only be populated if a p2wsh or a p2sh output is being signed.
|
||||||
|
WitnessScript []byte `protobuf:"bytes,4,opt,name=witness_script,json=witnessScript,proto3" json:"witness_script,omitempty"`
|
||||||
|
// *
|
||||||
|
// A description of the output being spent. The value and script MUST be provided.
|
||||||
|
Output *TxOut `protobuf:"bytes,5,opt,name=output" json:"output,omitempty"`
|
||||||
|
// *
|
||||||
|
// The target sighash type that should be used when generating the final
|
||||||
|
// sighash, and signature.
|
||||||
|
Sighash uint32 `protobuf:"varint,7,opt,name=sighash" json:"sighash,omitempty"`
|
||||||
|
// *
|
||||||
|
// The target input within the transaction that should be signed.
|
||||||
|
InputIndex int32 `protobuf:"varint,8,opt,name=input_index,json=inputIndex" json:"input_index,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SignDescriptor) Reset() { *m = SignDescriptor{} }
|
||||||
|
func (m *SignDescriptor) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*SignDescriptor) ProtoMessage() {}
|
||||||
|
func (*SignDescriptor) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||||
|
|
||||||
|
func (m *SignDescriptor) GetKeyDesc() *KeyDescriptor {
|
||||||
|
if m != nil {
|
||||||
|
return m.KeyDesc
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SignDescriptor) GetSingleTweak() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.SingleTweak
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SignDescriptor) GetDoubleTweak() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.DoubleTweak
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SignDescriptor) GetWitnessScript() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.WitnessScript
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SignDescriptor) GetOutput() *TxOut {
|
||||||
|
if m != nil {
|
||||||
|
return m.Output
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SignDescriptor) GetSighash() uint32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Sighash
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SignDescriptor) GetInputIndex() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.InputIndex
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignReq struct {
|
||||||
|
// / The raw bytes of the transaction to be signed.
|
||||||
|
RawTxBytes []byte `protobuf:"bytes,1,opt,name=raw_tx_bytes,json=rawTxBytes,proto3" json:"raw_tx_bytes,omitempty"`
|
||||||
|
// / A set of sign descriptors, for each input to be signed.
|
||||||
|
SignDescs []*SignDescriptor `protobuf:"bytes,2,rep,name=sign_descs,json=signDescs" json:"sign_descs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SignReq) Reset() { *m = SignReq{} }
|
||||||
|
func (m *SignReq) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*SignReq) ProtoMessage() {}
|
||||||
|
func (*SignReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
|
||||||
|
|
||||||
|
func (m *SignReq) GetRawTxBytes() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.RawTxBytes
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SignReq) GetSignDescs() []*SignDescriptor {
|
||||||
|
if m != nil {
|
||||||
|
return m.SignDescs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignResp struct {
|
||||||
|
// *
|
||||||
|
// A set of signatures realized in a fixed 64-byte format ordered in ascending
|
||||||
|
// input order.
|
||||||
|
RawSigs [][]byte `protobuf:"bytes,1,rep,name=raw_sigs,json=rawSigs,proto3" json:"raw_sigs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SignResp) Reset() { *m = SignResp{} }
|
||||||
|
func (m *SignResp) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*SignResp) ProtoMessage() {}
|
||||||
|
func (*SignResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
|
||||||
|
|
||||||
|
func (m *SignResp) GetRawSigs() [][]byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.RawSigs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type InputScript struct {
|
||||||
|
// / The serializes witness stack for the specified input.
|
||||||
|
Witness [][]byte `protobuf:"bytes,1,rep,name=witness,proto3" json:"witness,omitempty"`
|
||||||
|
// **
|
||||||
|
// The optional sig script for the specified witness that will only be set if
|
||||||
|
// the input specified is a nested p2sh witness program.
|
||||||
|
SigScript []byte `protobuf:"bytes,2,opt,name=sig_script,json=sigScript,proto3" json:"sig_script,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *InputScript) Reset() { *m = InputScript{} }
|
||||||
|
func (m *InputScript) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*InputScript) ProtoMessage() {}
|
||||||
|
func (*InputScript) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
|
||||||
|
|
||||||
|
func (m *InputScript) GetWitness() [][]byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.Witness
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *InputScript) GetSigScript() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.SigScript
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type InputScriptResp struct {
|
||||||
|
InputScripts []*InputScript `protobuf:"bytes,1,rep,name=input_scripts,json=inputScripts" json:"input_scripts,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *InputScriptResp) Reset() { *m = InputScriptResp{} }
|
||||||
|
func (m *InputScriptResp) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*InputScriptResp) ProtoMessage() {}
|
||||||
|
func (*InputScriptResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
|
||||||
|
|
||||||
|
func (m *InputScriptResp) GetInputScripts() []*InputScript {
|
||||||
|
if m != nil {
|
||||||
|
return m.InputScripts
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*KeyLocator)(nil), "signrpc.KeyLocator")
|
||||||
|
proto.RegisterType((*KeyDescriptor)(nil), "signrpc.KeyDescriptor")
|
||||||
|
proto.RegisterType((*TxOut)(nil), "signrpc.TxOut")
|
||||||
|
proto.RegisterType((*SignDescriptor)(nil), "signrpc.SignDescriptor")
|
||||||
|
proto.RegisterType((*SignReq)(nil), "signrpc.SignReq")
|
||||||
|
proto.RegisterType((*SignResp)(nil), "signrpc.SignResp")
|
||||||
|
proto.RegisterType((*InputScript)(nil), "signrpc.InputScript")
|
||||||
|
proto.RegisterType((*InputScriptResp)(nil), "signrpc.InputScriptResp")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ context.Context
|
||||||
|
var _ grpc.ClientConn
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
const _ = grpc.SupportPackageIsVersion4
|
||||||
|
|
||||||
|
// Client API for Signer service
|
||||||
|
|
||||||
|
type SignerClient interface {
|
||||||
|
// *
|
||||||
|
// SignOutputRaw is a method that can be used to generated a signature for a
|
||||||
|
// set of inputs/outputs to a transaction. Each request specifies details
|
||||||
|
// concerning how the outputs should be signed, which keys they should be
|
||||||
|
// signed with, and also any optional tweaks. The return value is a fixed
|
||||||
|
// 64-byte signature (the same format as we use on the wire in Lightning).
|
||||||
|
//
|
||||||
|
// If we are unable to sign using the specified keys, then an error will be
|
||||||
|
// returned.
|
||||||
|
SignOutputRaw(ctx context.Context, in *SignReq, opts ...grpc.CallOption) (*SignResp, error)
|
||||||
|
// *
|
||||||
|
// ComputeInputScript generates a complete InputIndex for the passed
|
||||||
|
// transaction with the signature as defined within the passed SignDescriptor.
|
||||||
|
// This method should be capable of generating the proper input script for
|
||||||
|
// both regular p2wkh output and p2wkh outputs nested within a regular p2sh
|
||||||
|
// output.
|
||||||
|
//
|
||||||
|
// Note that when using this method to sign inputs belonging to the wallet,
|
||||||
|
// the only items of the SignDescriptor that need to be populated are pkScript
|
||||||
|
// in the TxOut field, the value in that same field, and finally the input
|
||||||
|
// index.
|
||||||
|
ComputeInputScript(ctx context.Context, in *SignReq, opts ...grpc.CallOption) (*InputScriptResp, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type signerClient struct {
|
||||||
|
cc *grpc.ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSignerClient(cc *grpc.ClientConn) SignerClient {
|
||||||
|
return &signerClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *signerClient) SignOutputRaw(ctx context.Context, in *SignReq, opts ...grpc.CallOption) (*SignResp, error) {
|
||||||
|
out := new(SignResp)
|
||||||
|
err := grpc.Invoke(ctx, "/signrpc.Signer/SignOutputRaw", in, out, c.cc, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *signerClient) ComputeInputScript(ctx context.Context, in *SignReq, opts ...grpc.CallOption) (*InputScriptResp, error) {
|
||||||
|
out := new(InputScriptResp)
|
||||||
|
err := grpc.Invoke(ctx, "/signrpc.Signer/ComputeInputScript", in, out, c.cc, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server API for Signer service
|
||||||
|
|
||||||
|
type SignerServer interface {
|
||||||
|
// *
|
||||||
|
// SignOutputRaw is a method that can be used to generated a signature for a
|
||||||
|
// set of inputs/outputs to a transaction. Each request specifies details
|
||||||
|
// concerning how the outputs should be signed, which keys they should be
|
||||||
|
// signed with, and also any optional tweaks. The return value is a fixed
|
||||||
|
// 64-byte signature (the same format as we use on the wire in Lightning).
|
||||||
|
//
|
||||||
|
// If we are unable to sign using the specified keys, then an error will be
|
||||||
|
// returned.
|
||||||
|
SignOutputRaw(context.Context, *SignReq) (*SignResp, error)
|
||||||
|
// *
|
||||||
|
// ComputeInputScript generates a complete InputIndex for the passed
|
||||||
|
// transaction with the signature as defined within the passed SignDescriptor.
|
||||||
|
// This method should be capable of generating the proper input script for
|
||||||
|
// both regular p2wkh output and p2wkh outputs nested within a regular p2sh
|
||||||
|
// output.
|
||||||
|
//
|
||||||
|
// Note that when using this method to sign inputs belonging to the wallet,
|
||||||
|
// the only items of the SignDescriptor that need to be populated are pkScript
|
||||||
|
// in the TxOut field, the value in that same field, and finally the input
|
||||||
|
// index.
|
||||||
|
ComputeInputScript(context.Context, *SignReq) (*InputScriptResp, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterSignerServer(s *grpc.Server, srv SignerServer) {
|
||||||
|
s.RegisterService(&_Signer_serviceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Signer_SignOutputRaw_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(SignReq)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(SignerServer).SignOutputRaw(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/signrpc.Signer/SignOutputRaw",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(SignerServer).SignOutputRaw(ctx, req.(*SignReq))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Signer_ComputeInputScript_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(SignReq)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(SignerServer).ComputeInputScript(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/signrpc.Signer/ComputeInputScript",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(SignerServer).ComputeInputScript(ctx, req.(*SignReq))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _Signer_serviceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "signrpc.Signer",
|
||||||
|
HandlerType: (*SignerServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "SignOutputRaw",
|
||||||
|
Handler: _Signer_SignOutputRaw_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "ComputeInputScript",
|
||||||
|
Handler: _Signer_ComputeInputScript_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "signrpc/signer.proto",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("signrpc/signer.proto", fileDescriptor0) }
|
||||||
|
|
||||||
|
var fileDescriptor0 = []byte{
|
||||||
|
// 536 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x93, 0xd1, 0x8f, 0xd2, 0x40,
|
||||||
|
0x10, 0xc6, 0x0f, 0x10, 0xca, 0x4d, 0x5b, 0xd4, 0x95, 0x68, 0xd5, 0x18, 0xb1, 0xf1, 0x0c, 0x4f,
|
||||||
|
0x18, 0xd1, 0x98, 0xe8, 0x93, 0x39, 0xcd, 0x85, 0x0b, 0x97, 0x5c, 0xb2, 0xf0, 0xde, 0x94, 0xb2,
|
||||||
|
0xf6, 0x36, 0xe5, 0xda, 0x5e, 0x77, 0x6b, 0xe9, 0x9b, 0xff, 0x83, 0xff, 0xb0, 0x99, 0xdd, 0x05,
|
||||||
|
0x8a, 0xde, 0x13, 0x7c, 0x5f, 0x67, 0x67, 0x7e, 0x3b, 0x5f, 0x0b, 0x43, 0xc1, 0xe3, 0xb4, 0xc8,
|
||||||
|
0xa3, 0xf7, 0xf8, 0xcb, 0x8a, 0x49, 0x5e, 0x64, 0x32, 0x23, 0x96, 0x71, 0xfd, 0x19, 0xc0, 0x9c,
|
||||||
|
0xd5, 0x57, 0x59, 0x14, 0xca, 0xac, 0x20, 0xaf, 0x00, 0x12, 0x56, 0x07, 0x3f, 0xc3, 0x5b, 0xbe,
|
||||||
|
0xa9, 0xbd, 0xd6, 0xa8, 0x35, 0xee, 0xd2, 0xd3, 0x84, 0xd5, 0x17, 0xca, 0x20, 0x2f, 0x01, 0x45,
|
||||||
|
0xc0, 0xd3, 0x35, 0xdb, 0x7a, 0x6d, 0xf5, 0xb4, 0x9f, 0xb0, 0xfa, 0x12, 0xb5, 0xbf, 0x01, 0x77,
|
||||||
|
0xce, 0xea, 0x1f, 0x4c, 0x44, 0x05, 0xcf, 0xb1, 0xd9, 0x5b, 0x70, 0x8b, 0xb0, 0x0a, 0xf0, 0xc4,
|
||||||
|
0xaa, 0x96, 0x4c, 0xa8, 0x7e, 0xce, 0xec, 0x84, 0xda, 0x45, 0x58, 0xcd, 0x59, 0x7d, 0x8e, 0x26,
|
||||||
|
0x99, 0x80, 0x85, 0x15, 0x9b, 0x2c, 0x52, 0x1d, 0xed, 0xe9, 0x93, 0x89, 0x61, 0x9b, 0x1c, 0xc0,
|
||||||
|
0x66, 0x27, 0xb4, 0x97, 0x28, 0x75, 0xde, 0x85, 0x4e, 0xc2, 0x6a, 0xff, 0x2b, 0x74, 0x97, 0xdb,
|
||||||
|
0xeb, 0x52, 0x92, 0x21, 0x74, 0x7f, 0x85, 0x9b, 0x92, 0xa9, 0xee, 0x1d, 0xaa, 0x05, 0x92, 0xe6,
|
||||||
|
0x49, 0xa0, 0x51, 0x54, 0x5f, 0x87, 0xf6, 0xf3, 0x64, 0xa1, 0xb4, 0xff, 0xa7, 0x0d, 0x83, 0x05,
|
||||||
|
0x8f, 0xd3, 0x06, 0xeb, 0x07, 0xc0, 0x8b, 0x04, 0x6b, 0x26, 0x22, 0xd5, 0xc8, 0x9e, 0x3e, 0x6d,
|
||||||
|
0x62, 0x1c, 0x2a, 0x29, 0xd2, 0xa2, 0x24, 0x6f, 0xc0, 0x11, 0x3c, 0x8d, 0x37, 0x2c, 0x90, 0x15,
|
||||||
|
0x0b, 0x13, 0x33, 0xc5, 0xd6, 0xde, 0x12, 0x2d, 0x2c, 0x59, 0x67, 0xe5, 0x6a, 0x5f, 0xd2, 0xd1,
|
||||||
|
0x25, 0xda, 0xd3, 0x25, 0x67, 0x30, 0xa8, 0xb8, 0x4c, 0x99, 0x10, 0x3b, 0xda, 0x07, 0xaa, 0xc8,
|
||||||
|
0x35, 0xae, 0x46, 0x26, 0xef, 0xa0, 0x97, 0x95, 0x32, 0x2f, 0xa5, 0xd7, 0x55, 0x74, 0x83, 0x3d,
|
||||||
|
0x9d, 0xda, 0x02, 0x35, 0x4f, 0x89, 0x07, 0x98, 0xec, 0x4d, 0x28, 0x6e, 0x3c, 0x6b, 0xd4, 0x1a,
|
||||||
|
0xbb, 0x74, 0x27, 0xc9, 0x6b, 0xb0, 0x79, 0x9a, 0x97, 0xd2, 0xa4, 0xd7, 0x57, 0xe9, 0x81, 0xb2,
|
||||||
|
0x74, 0x7e, 0x11, 0x58, 0xb8, 0x14, 0xca, 0xee, 0xc8, 0x08, 0x1c, 0x4c, 0x4e, 0x6e, 0x9b, 0xc1,
|
||||||
|
0x51, 0x28, 0xc2, 0x6a, 0xb9, 0xd5, 0xa9, 0x7d, 0x06, 0x40, 0x00, 0xb5, 0x30, 0xe1, 0xb5, 0x47,
|
||||||
|
0x9d, 0xb1, 0x3d, 0x7d, 0xb6, 0x67, 0x3a, 0x5e, 0x2e, 0x3d, 0x15, 0x46, 0x0b, 0xff, 0x0c, 0xfa,
|
||||||
|
0x7a, 0x88, 0xc8, 0xc9, 0x73, 0xe8, 0xe3, 0x14, 0xc1, 0x63, 0x9c, 0xd0, 0x19, 0x3b, 0xd4, 0x2a,
|
||||||
|
0xc2, 0x6a, 0xc1, 0x63, 0xe1, 0x5f, 0x80, 0x7d, 0x89, 0x64, 0xe6, 0xf6, 0x1e, 0x58, 0x66, 0x1d,
|
||||||
|
0xbb, 0x42, 0x23, 0xf1, 0x85, 0x15, 0x3c, 0x3e, 0x0e, 0x1a, 0xc7, 0x99, 0xa4, 0xaf, 0xe0, 0x61,
|
||||||
|
0xa3, 0x8f, 0x9a, 0xfa, 0x05, 0x5c, 0xbd, 0x07, 0x7d, 0x46, 0x77, 0xb4, 0xa7, 0xc3, 0x3d, 0x7c,
|
||||||
|
0xf3, 0x80, 0xc3, 0x0f, 0x42, 0x4c, 0x7f, 0xb7, 0xa0, 0xb7, 0x50, 0x5f, 0x11, 0xf9, 0x04, 0x2e,
|
||||||
|
0xfe, 0xbb, 0x56, 0x5b, 0xa7, 0x61, 0x45, 0x1e, 0x1d, 0x5d, 0x9e, 0xb2, 0xbb, 0x17, 0x8f, 0xff,
|
||||||
|
0x71, 0x44, 0x4e, 0xbe, 0x01, 0xf9, 0x9e, 0xdd, 0xe6, 0xa5, 0x64, 0xcd, 0xdb, 0xfd, 0x7f, 0xd4,
|
||||||
|
0xbb, 0x17, 0x86, 0x89, 0x7c, 0xd5, 0x53, 0x9f, 0xef, 0xc7, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff,
|
||||||
|
0x7b, 0x48, 0x93, 0x2a, 0xd6, 0x03, 0x00, 0x00,
|
||||||
|
}
|
148
lnrpc/signrpc/signer.proto
Normal file
148
lnrpc/signrpc/signer.proto
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package signrpc;
|
||||||
|
|
||||||
|
message KeyLocator {
|
||||||
|
/// The family of key being identified.
|
||||||
|
int32 key_family = 1;
|
||||||
|
|
||||||
|
/// The precise index of the key being identified.
|
||||||
|
int32 key_index = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message KeyDescriptor {
|
||||||
|
oneof key {
|
||||||
|
/**
|
||||||
|
The raw bytes of the key being identified. Either this or the KeyLocator
|
||||||
|
must be specified.
|
||||||
|
*/
|
||||||
|
bytes raw_key_bytes = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
The key locator that identifies which key to use for signing. Either this
|
||||||
|
or the raw bytes of the target key must be specified.
|
||||||
|
*/
|
||||||
|
KeyLocator key_loc = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message TxOut {
|
||||||
|
/// The value of the output being spent.
|
||||||
|
int64 value = 1;
|
||||||
|
|
||||||
|
/// The script of the output being spent.
|
||||||
|
bytes pk_script = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SignDescriptor {
|
||||||
|
/**
|
||||||
|
A descriptor that precisely describes *which* key to use for signing. This
|
||||||
|
may provide the raw public key directly, or require the Signer to re-derive
|
||||||
|
the key according to the populated derivation path.
|
||||||
|
*/
|
||||||
|
KeyDescriptor key_desc = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A scalar value that will be added to the private key corresponding to the
|
||||||
|
above public key to obtain the private key to be used to sign this input.
|
||||||
|
This value is typically derived via the following computation:
|
||||||
|
|
||||||
|
* derivedKey = privkey + sha256(perCommitmentPoint || pubKey) mod N
|
||||||
|
*/
|
||||||
|
bytes single_tweak = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A private key that will be used in combination with its corresponding
|
||||||
|
private key to derive the private key that is to be used to sign the target
|
||||||
|
input. Within the Lightning protocol, this value is typically the
|
||||||
|
commitment secret from a previously revoked commitment transaction. This
|
||||||
|
value is in combination with two hash values, and the original private key
|
||||||
|
to derive the private key to be used when signing.
|
||||||
|
|
||||||
|
* k = (privKey*sha256(pubKey || tweakPub) +
|
||||||
|
tweakPriv*sha256(tweakPub || pubKey)) mod N
|
||||||
|
*/
|
||||||
|
bytes double_tweak = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
The full script required to properly redeem the output. This field will
|
||||||
|
only be populated if a p2wsh or a p2sh output is being signed.
|
||||||
|
*/
|
||||||
|
bytes witness_script = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A description of the output being spent. The value and script MUST be provided.
|
||||||
|
*/
|
||||||
|
TxOut output = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
The target sighash type that should be used when generating the final
|
||||||
|
sighash, and signature.
|
||||||
|
*/
|
||||||
|
uint32 sighash = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
The target input within the transaction that should be signed.
|
||||||
|
*/
|
||||||
|
int32 input_index = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SignReq {
|
||||||
|
/// The raw bytes of the transaction to be signed.
|
||||||
|
bytes raw_tx_bytes = 1;
|
||||||
|
|
||||||
|
/// A set of sign descriptors, for each input to be signed.
|
||||||
|
repeated SignDescriptor sign_descs = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SignResp {
|
||||||
|
/**
|
||||||
|
A set of signatures realized in a fixed 64-byte format ordered in ascending
|
||||||
|
input order.
|
||||||
|
*/
|
||||||
|
repeated bytes raw_sigs = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message InputScript {
|
||||||
|
/// The serializes witness stack for the specified input.
|
||||||
|
repeated bytes witness = 1;
|
||||||
|
|
||||||
|
/***
|
||||||
|
The optional sig script for the specified witness that will only be set if
|
||||||
|
the input specified is a nested p2sh witness program.
|
||||||
|
*/
|
||||||
|
bytes sig_script = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message InputScriptResp {
|
||||||
|
/// The set of fully valid input scripts requested.
|
||||||
|
repeated InputScript input_scripts = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service Signer {
|
||||||
|
/**
|
||||||
|
SignOutputRaw is a method that can be used to generated a signature for a
|
||||||
|
set of inputs/outputs to a transaction. Each request specifies details
|
||||||
|
concerning how the outputs should be signed, which keys they should be
|
||||||
|
signed with, and also any optional tweaks. The return value is a fixed
|
||||||
|
64-byte signature (the same format as we use on the wire in Lightning).
|
||||||
|
|
||||||
|
If we are unable to sign using the specified keys, then an error will be
|
||||||
|
returned.
|
||||||
|
*/
|
||||||
|
rpc SignOutputRaw(SignReq) returns (SignResp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
ComputeInputScript generates a complete InputIndex for the passed
|
||||||
|
transaction with the signature as defined within the passed SignDescriptor.
|
||||||
|
This method should be capable of generating the proper input script for
|
||||||
|
both regular p2wkh output and p2wkh outputs nested within a regular p2sh
|
||||||
|
output.
|
||||||
|
|
||||||
|
Note that when using this method to sign inputs belonging to the wallet,
|
||||||
|
the only items of the SignDescriptor that need to be populated are pkScript
|
||||||
|
in the TxOut field, the value in that same field, and finally the input
|
||||||
|
index.
|
||||||
|
*/
|
||||||
|
rpc ComputeInputScript(SignReq) returns (InputScriptResp);
|
||||||
|
}
|
390
lnrpc/signrpc/signer_server.go
Normal file
390
lnrpc/signrpc/signer_server.go
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
// +build signrpc
|
||||||
|
|
||||||
|
package signrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcec"
|
||||||
|
"github.com/btcsuite/btcd/txscript"
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// subServerName is the name of the sub rpc server. We'll use this name
|
||||||
|
// to register ourselves, and we also require that the main
|
||||||
|
// SubServerConfigDispatcher instance recognize this as the name of the
|
||||||
|
// config file that we need.
|
||||||
|
subServerName = "SignRPC"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// macaroonOps are the set of capabilities that our minted macaroon (if
|
||||||
|
// it doesn't already exist) will have.
|
||||||
|
macaroonOps = []bakery.Op{
|
||||||
|
{
|
||||||
|
Entity: "signer",
|
||||||
|
Action: "generate",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// macPermissions maps RPC calls to the permissions they require.
|
||||||
|
macPermissions = map[string][]bakery.Op{
|
||||||
|
"/signrpc.Signer/SignOutputRaw": {{
|
||||||
|
Entity: "signer",
|
||||||
|
Action: "generate",
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultSignerMacFilename is the default name of the signer macaroon
|
||||||
|
// that we expect to find via a file handle within the main
|
||||||
|
// configuration file in this package.
|
||||||
|
DefaultSignerMacFilename = "signer.macaroon"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Server is a sub-server of the main RPC server: the signer RPC. This sub RPC
|
||||||
|
// server allows external callers to access the full signing 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 {
|
||||||
|
cfg *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// A compile time check to ensure that Server fully implements the SignerServer
|
||||||
|
// gRPC service.
|
||||||
|
var _ SignerServer = (*Server)(nil)
|
||||||
|
|
||||||
|
// fileExists reports whether the named file or directory exists.
|
||||||
|
func fileExists(name string) bool {
|
||||||
|
if _, err := os.Stat(name); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new instance of the signrpc Signer sub-server. We also return
|
||||||
|
// the set of permissions for the macaroons that we may create within this
|
||||||
|
// method. If the macaroons we need aren't found in the filepath, then we'll
|
||||||
|
// create them on start up. If we're unable to locate, or create the macaroons
|
||||||
|
// we need, then we'll return with an error.
|
||||||
|
func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) {
|
||||||
|
// If the path of the signer macaroon wasn't generated, then we'll
|
||||||
|
// assume that it's found at the default network directory.
|
||||||
|
if cfg.SignerMacPath == "" {
|
||||||
|
cfg.SignerMacPath = filepath.Join(
|
||||||
|
cfg.NetworkDir, DefaultSignerMacFilename,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we know the full path of the signer macaroon, we can check
|
||||||
|
// to see if we need to create it or not.
|
||||||
|
macFilePath := cfg.SignerMacPath
|
||||||
|
if cfg.MacService != nil && !fileExists(macFilePath) {
|
||||||
|
log.Infof("Making macaroons for Signer RPC Server at: %v",
|
||||||
|
macFilePath)
|
||||||
|
|
||||||
|
// At this point, we know that the signer macaroon doesn't yet,
|
||||||
|
// exist, so we need to create it with the help of the main
|
||||||
|
// macaroon service.
|
||||||
|
signerMac, err := cfg.MacService.Oven.NewMacaroon(
|
||||||
|
context.Background(), bakery.LatestVersion, nil,
|
||||||
|
macaroonOps...,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
signerMacBytes, err := signerMac.M().MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(macFilePath, signerMacBytes, 0644)
|
||||||
|
if err != nil {
|
||||||
|
os.Remove(macFilePath)
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signerServer := &Server{
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
return signerServer, macPermissions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start launches any helper goroutines required for the rpcServer to function.
|
||||||
|
//
|
||||||
|
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||||
|
func (s *Server) Start() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop signals any active goroutines for a graceful closure.
|
||||||
|
//
|
||||||
|
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||||
|
func (s *Server) Stop() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns a unique string representation of the sub-server. This can be
|
||||||
|
// used to identify the sub-server and also de-duplicate them.
|
||||||
|
//
|
||||||
|
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||||
|
func (s *Server) Name() string {
|
||||||
|
return subServerName
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterWithRootServer will be called by the root gRPC server to direct a
|
||||||
|
// sub RPC server to register itself with the main gRPC root server. Until this
|
||||||
|
// is called, each sub-server won't be able to have
|
||||||
|
// requests routed towards it.
|
||||||
|
//
|
||||||
|
// NOTE: This is part of the lnrpc.SubServer interface.
|
||||||
|
func (s *Server) 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.
|
||||||
|
RegisterSignerServer(grpcServer, s)
|
||||||
|
|
||||||
|
log.Debugf("Signer RPC server successfully register with root gRPC " +
|
||||||
|
"server")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignOutputRaw generates a signature for the passed transaction according to
|
||||||
|
// the data within the passed SignReq. If we're unable to find the keys that
|
||||||
|
// correspond to the KeyLocators in the SignReq then we'll return an error.
|
||||||
|
// Additionally, if the user doesn't provide the set of required parameters, or
|
||||||
|
// provides an invalid transaction, then we'll return with an error.
|
||||||
|
//
|
||||||
|
// NOTE: The resulting signature should be void of a sighash byte.
|
||||||
|
func (s *Server) SignOutputRaw(ctx context.Context, in *SignReq) (*SignResp, error) {
|
||||||
|
|
||||||
|
switch {
|
||||||
|
// If the client doesn't specify a transaction, then there's nothing to
|
||||||
|
// sign, so we'll exit early.
|
||||||
|
case len(in.RawTxBytes) == 0:
|
||||||
|
return nil, fmt.Errorf("a transaction to sign MUST be " +
|
||||||
|
"passed in")
|
||||||
|
|
||||||
|
// If the client doesn't tell us *how* to sign the transaction, then we
|
||||||
|
// can't sign anything, so we'll exit early.
|
||||||
|
case len(in.SignDescs) == 0:
|
||||||
|
return nil, fmt.Errorf("at least one SignDescs MUST be " +
|
||||||
|
"passed in")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we know we have an actual transaction to decode, we'll
|
||||||
|
// deserialize it into something that we can properly utilize.
|
||||||
|
var (
|
||||||
|
txToSign wire.MsgTx
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
txReader := bytes.NewReader(in.RawTxBytes)
|
||||||
|
if err := txToSign.Deserialize(txReader); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to decode tx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sigHashCache := txscript.NewTxSigHashes(&txToSign)
|
||||||
|
|
||||||
|
log.Debugf("Generating sigs for %v inputs: ", len(in.SignDescs))
|
||||||
|
|
||||||
|
// With the transaction deserialized, we'll now convert sign descs so
|
||||||
|
// we can feed it into the actual signer.
|
||||||
|
signDescs := make([]*lnwallet.SignDescriptor, 0, len(in.SignDescs))
|
||||||
|
for _, signDesc := range in.SignDescs {
|
||||||
|
keyDesc := signDesc.KeyDesc
|
||||||
|
|
||||||
|
// The caller can either specify the key using the raw pubkey,
|
||||||
|
// or the description of the key. Below we'll feel out the
|
||||||
|
// oneof field to decide which one we will attempt to parse.
|
||||||
|
var (
|
||||||
|
targetPubKey *btcec.PublicKey
|
||||||
|
keyLoc keychain.KeyLocator
|
||||||
|
)
|
||||||
|
switch {
|
||||||
|
|
||||||
|
// If this method doesn't return nil, then we know that user is
|
||||||
|
// attempting to include a raw serialized pub key.
|
||||||
|
case keyDesc.GetRawKeyBytes() != nil:
|
||||||
|
rawKeyBytes := keyDesc.GetRawKeyBytes()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
// If the user provided a raw key, but it's of the
|
||||||
|
// wrong length, then we'll return with an error.
|
||||||
|
case len(rawKeyBytes) != 0 && len(rawKeyBytes) != 33:
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("pubkey must be " +
|
||||||
|
"serialized in compressed format if " +
|
||||||
|
"specified")
|
||||||
|
|
||||||
|
// If a proper raw key was provided, then we'll attempt
|
||||||
|
// to decode and parse it.
|
||||||
|
case len(rawKeyBytes) != 0 && len(rawKeyBytes) == 33:
|
||||||
|
targetPubKey, err = btcec.ParsePubKey(
|
||||||
|
rawKeyBytes, btcec.S256(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to "+
|
||||||
|
"parse pubkey: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similarly, if they specified a key locator, then we'll use
|
||||||
|
// that instead.
|
||||||
|
case keyDesc.GetKeyLoc() != nil:
|
||||||
|
protoLoc := keyDesc.GetKeyLoc()
|
||||||
|
keyLoc = keychain.KeyLocator{
|
||||||
|
Family: keychain.KeyFamily(
|
||||||
|
protoLoc.KeyFamily,
|
||||||
|
),
|
||||||
|
Index: uint32(protoLoc.KeyIndex),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a witness script isn't passed, then we can't proceed, as
|
||||||
|
// in the p2wsh case, we can't properly generate the sighash.
|
||||||
|
if len(signDesc.WitnessScript) == 0 {
|
||||||
|
// TODO(roasbeef): if regualr p2wkh, then at times
|
||||||
|
// internally we allow script to go by
|
||||||
|
return nil, fmt.Errorf("witness script MUST be " +
|
||||||
|
"specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the users provided a double tweak, then we'll need to
|
||||||
|
// parse that out now to ensure their input is properly signed.
|
||||||
|
var tweakPrivKey *btcec.PrivateKey
|
||||||
|
if len(signDesc.DoubleTweak) != 0 {
|
||||||
|
tweakPrivKey, _ = btcec.PrivKeyFromBytes(
|
||||||
|
btcec.S256(), signDesc.DoubleTweak,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, with verification and parsing complete, we can
|
||||||
|
// construct the final sign descriptor to generate the proper
|
||||||
|
// signature for this input.
|
||||||
|
signDescs = append(signDescs, &lnwallet.SignDescriptor{
|
||||||
|
KeyDesc: keychain.KeyDescriptor{
|
||||||
|
KeyLocator: keyLoc,
|
||||||
|
PubKey: targetPubKey,
|
||||||
|
},
|
||||||
|
SingleTweak: signDesc.SingleTweak,
|
||||||
|
DoubleTweak: tweakPrivKey,
|
||||||
|
WitnessScript: signDesc.WitnessScript,
|
||||||
|
Output: &wire.TxOut{
|
||||||
|
Value: signDesc.Output.Value,
|
||||||
|
PkScript: signDesc.Output.PkScript,
|
||||||
|
},
|
||||||
|
HashType: txscript.SigHashType(signDesc.Sighash),
|
||||||
|
SigHashes: sigHashCache,
|
||||||
|
InputIndex: int(signDesc.InputIndex),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we've mapped all the proper sign descriptors, we can
|
||||||
|
// request signatures for each of them, passing in the transaction to
|
||||||
|
// be signed.
|
||||||
|
numSigs := len(in.SignDescs)
|
||||||
|
resp := &SignResp{
|
||||||
|
RawSigs: make([][]byte, numSigs),
|
||||||
|
}
|
||||||
|
for i, signDesc := range signDescs {
|
||||||
|
sig, err := s.cfg.Signer.SignOutputRaw(&txToSign, signDesc)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable to generate sig for input "+
|
||||||
|
"#%v: %v", i, err)
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.RawSigs[i] = sig
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ComputeInputScript generates a complete InputIndex for the passed
|
||||||
|
// transaction with the signature as defined within the passed SignDescriptor.
|
||||||
|
// This method should be capable of generating the proper input script for both
|
||||||
|
// regular p2wkh output and p2wkh outputs nested within a regular p2sh output.
|
||||||
|
//
|
||||||
|
// Note that when using this method to sign inputs belonging to the wallet, the
|
||||||
|
// only items of the SignDescriptor that need to be populated are pkScript in
|
||||||
|
// the TxOut field, the value in that same field, and finally the input index.
|
||||||
|
func (s *Server) ComputeInputScript(ctx context.Context,
|
||||||
|
in *SignReq) (*InputScriptResp, error) {
|
||||||
|
|
||||||
|
switch {
|
||||||
|
// If the client doesn't specify a transaction, then there's nothing to
|
||||||
|
// sign, so we'll exit early.
|
||||||
|
case len(in.RawTxBytes) == 0:
|
||||||
|
return nil, fmt.Errorf("a transaction to sign MUST be " +
|
||||||
|
"passed in")
|
||||||
|
|
||||||
|
// If the client doesn't tell us *how* to sign the transaction, then we
|
||||||
|
// can't sign anything, so we'll exit early.
|
||||||
|
case len(in.SignDescs) == 0:
|
||||||
|
return nil, fmt.Errorf("at least one SignDescs MUST be " +
|
||||||
|
"passed in")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we know we have an actual transaction to decode, we'll
|
||||||
|
// deserialize it into something that we can properly utilize.
|
||||||
|
var txToSign wire.MsgTx
|
||||||
|
txReader := bytes.NewReader(in.RawTxBytes)
|
||||||
|
if err := txToSign.Deserialize(txReader); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to decode tx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sigHashCache := txscript.NewTxSigHashes(&txToSign)
|
||||||
|
|
||||||
|
signDescs := make([]*lnwallet.SignDescriptor, 0, len(in.SignDescs))
|
||||||
|
for _, signDesc := range in.SignDescs {
|
||||||
|
// For this method, the only fields that we care about are the
|
||||||
|
// hash type, and the information concerning the output as we
|
||||||
|
// only know how to provide full witnesses for outputs that we
|
||||||
|
// solely control.
|
||||||
|
signDescs = append(signDescs, &lnwallet.SignDescriptor{
|
||||||
|
Output: &wire.TxOut{
|
||||||
|
Value: signDesc.Output.Value,
|
||||||
|
PkScript: signDesc.Output.PkScript,
|
||||||
|
},
|
||||||
|
HashType: txscript.SigHashType(signDesc.Sighash),
|
||||||
|
SigHashes: sigHashCache,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// With all of our signDescs assembled, we can now generate a valid
|
||||||
|
// input script for each of them, and collate the responses to return
|
||||||
|
// back to the caller.
|
||||||
|
numWitnesses := len(in.SignDescs)
|
||||||
|
resp := &InputScriptResp{
|
||||||
|
InputScripts: make([]*InputScript, numWitnesses),
|
||||||
|
}
|
||||||
|
for i, signDesc := range signDescs {
|
||||||
|
inputScript, err := s.cfg.Signer.ComputeInputScript(
|
||||||
|
&txToSign, signDesc,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.InputScripts[i] = &InputScript{
|
||||||
|
Witness: inputScript.Witness,
|
||||||
|
SigScript: inputScript.ScriptSig,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
131
lnrpc/sub_server.go
Normal file
131
lnrpc/sub_server.go
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
package lnrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MacaroonPerms is a map from the FullMethod of an invoked gRPC command. It
|
||||||
|
// maps the set of operations that the macaroon presented with the command MUST
|
||||||
|
// satisfy. With this map, all sub-servers are able to communicate to the
|
||||||
|
// primary macaroon service what type of macaroon must be passed with each
|
||||||
|
// method present on the service of the sub-server.
|
||||||
|
type MacaroonPerms map[string][]bakery.Op
|
||||||
|
|
||||||
|
// SubServer is a child server of the main lnrpc gRPC server. Sub-servers allow
|
||||||
|
// lnd to expose discrete services that can be used with or independent of the
|
||||||
|
// main RPC server. The main rpcserver will create, start, stop, and manage
|
||||||
|
// each sub-server in a generalized manner.
|
||||||
|
type SubServer interface {
|
||||||
|
// Start starts the sub-server and all goroutines it needs to operate.
|
||||||
|
Start() error
|
||||||
|
|
||||||
|
// Stop signals that the sub-server should wrap up any lingering
|
||||||
|
// requests, and being a graceful shutdown.
|
||||||
|
Stop() error
|
||||||
|
|
||||||
|
// Name returns a unique string representation of the sub-server. This
|
||||||
|
// can be used to identify the sub-server and also de-duplicate them.
|
||||||
|
Name() string
|
||||||
|
|
||||||
|
// RegisterWithRootServer will be called by the root gRPC server to
|
||||||
|
// direct a sub RPC server to register itself with the main gRPC root
|
||||||
|
// server. Until this is called, each sub-server won't be able to have
|
||||||
|
// requests routed towards it.
|
||||||
|
RegisterWithRootServer(*grpc.Server) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubServerConfigDispatcher is an interface that all sub-servers will use to
|
||||||
|
// dynamically locate their configuration files. This abstraction will allow
|
||||||
|
// the primary RPC sever to initialize all sub-servers in a generic manner
|
||||||
|
// without knowing of each individual sub server.
|
||||||
|
type SubServerConfigDispatcher interface {
|
||||||
|
// FetchConfig attempts to locate an existing configuration file mapped
|
||||||
|
// to the target sub-server. If we're unable to find a config file
|
||||||
|
// matching the subServerName name, then false will be returned for the
|
||||||
|
// second parameter.
|
||||||
|
FetchConfig(subServerName string) (interface{}, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubServerDriver is a template struct that allows the root server to create a
|
||||||
|
// sub-server with minimal knowledge. The root server only need a fully
|
||||||
|
// populated SubServerConfigDispatcher and with the aide of the
|
||||||
|
// RegisterSubServers method, it's able to create and initialize all
|
||||||
|
// sub-servers.
|
||||||
|
type SubServerDriver struct {
|
||||||
|
// SubServerName is the full name of a sub-sever.
|
||||||
|
//
|
||||||
|
// NOTE: This MUST be unique.
|
||||||
|
SubServerName string
|
||||||
|
|
||||||
|
// New creates, and fully initializes a new sub-server instance with
|
||||||
|
// the aide of the SubServerConfigDispatcher. This closure should
|
||||||
|
// return the SubServer, ready for action, along with the set of
|
||||||
|
// macaroon permissions that the sub-server wishes to pass on to the
|
||||||
|
// root server for all methods routed towards it.
|
||||||
|
New func(subCfgs SubServerConfigDispatcher) (SubServer, MacaroonPerms, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// subServers is a package level global variable that houses all the
|
||||||
|
// registered sub-servers.
|
||||||
|
subServers = make(map[string]*SubServerDriver)
|
||||||
|
|
||||||
|
// registerMtx is a mutex that protects access to the above subServer
|
||||||
|
// map.
|
||||||
|
registerMtx sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisteredSubServers returns all registered sub-servers.
|
||||||
|
//
|
||||||
|
// NOTE: This function is safe for concurrent access.
|
||||||
|
func RegisteredSubServers() []*SubServerDriver {
|
||||||
|
registerMtx.Lock()
|
||||||
|
defer registerMtx.Unlock()
|
||||||
|
|
||||||
|
drivers := make([]*SubServerDriver, 0, len(subServers))
|
||||||
|
for _, driver := range subServers {
|
||||||
|
drivers = append(drivers, driver)
|
||||||
|
}
|
||||||
|
|
||||||
|
return drivers
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterSubServer should be called by a sub-server within its package's
|
||||||
|
// init() method to register its existence with the main sub-server map. Each
|
||||||
|
// sub-server, if active, is meant to register via this method in their init()
|
||||||
|
// method. This allows callers to easily initialize and register all
|
||||||
|
// sub-servers without knowing any details beyond that the fact that they
|
||||||
|
// satisfy the necessary interfaces.
|
||||||
|
//
|
||||||
|
// NOTE: This function is safe for concurrent access.
|
||||||
|
func RegisterSubServer(driver *SubServerDriver) error {
|
||||||
|
registerMtx.Lock()
|
||||||
|
defer registerMtx.Unlock()
|
||||||
|
|
||||||
|
if _, ok := subServers[driver.SubServerName]; ok {
|
||||||
|
return fmt.Errorf("subserver already registered")
|
||||||
|
}
|
||||||
|
|
||||||
|
subServers[driver.SubServerName] = driver
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportedServers returns slice of the names of all registered sub-servers.
|
||||||
|
//
|
||||||
|
// NOTE: This function is safe for concurrent access.
|
||||||
|
func SupportedServers() []string {
|
||||||
|
registerMtx.Lock()
|
||||||
|
defer registerMtx.Unlock()
|
||||||
|
|
||||||
|
supportedSubServers := make([]string, 0, len(subServers))
|
||||||
|
for driverName := range subServers {
|
||||||
|
supportedSubServers = append(supportedSubServers, driverName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return supportedSubServers
|
||||||
|
}
|
4
log.go
4
log.go
@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/contractcourt"
|
"github.com/lightningnetwork/lnd/contractcourt"
|
||||||
"github.com/lightningnetwork/lnd/discovery"
|
"github.com/lightningnetwork/lnd/discovery"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||||
|
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/routing"
|
"github.com/lightningnetwork/lnd/routing"
|
||||||
"github.com/lightningnetwork/lnd/signal"
|
"github.com/lightningnetwork/lnd/signal"
|
||||||
@ -65,6 +66,7 @@ var (
|
|||||||
cnctLog = build.NewSubLogger("CNCT", backendLog.Logger)
|
cnctLog = build.NewSubLogger("CNCT", backendLog.Logger)
|
||||||
sphxLog = build.NewSubLogger("SPHX", backendLog.Logger)
|
sphxLog = build.NewSubLogger("SPHX", backendLog.Logger)
|
||||||
swprLog = build.NewSubLogger("SWPR", backendLog.Logger)
|
swprLog = build.NewSubLogger("SWPR", backendLog.Logger)
|
||||||
|
sgnrLog = build.NewSubLogger("SGNR", backendLog.Logger)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Initialize package-global logger variables.
|
// Initialize package-global logger variables.
|
||||||
@ -82,6 +84,7 @@ func init() {
|
|||||||
sphinx.UseLogger(sphxLog)
|
sphinx.UseLogger(sphxLog)
|
||||||
signal.UseLogger(ltndLog)
|
signal.UseLogger(ltndLog)
|
||||||
sweep.UseLogger(swprLog)
|
sweep.UseLogger(swprLog)
|
||||||
|
signrpc.UseLogger(sgnrLog)
|
||||||
}
|
}
|
||||||
|
|
||||||
// subsystemLoggers maps each subsystem identifier to its associated logger.
|
// subsystemLoggers maps each subsystem identifier to its associated logger.
|
||||||
@ -105,6 +108,7 @@ var subsystemLoggers = map[string]btclog.Logger{
|
|||||||
"CNCT": cnctLog,
|
"CNCT": cnctLog,
|
||||||
"SPHX": sphxLog,
|
"SPHX": sphxLog,
|
||||||
"SWPR": swprLog,
|
"SWPR": swprLog,
|
||||||
|
"SGNR": sgnrLog,
|
||||||
}
|
}
|
||||||
|
|
||||||
// initLogRotator initializes the logging rotator to write logs to logFile and
|
// initLogRotator initializes the logging rotator to write logs to logFile and
|
||||||
|
@ -140,8 +140,9 @@ func (svc *Service) StreamServerInterceptor(
|
|||||||
"for method", info.FullMethod)
|
"for method", info.FullMethod)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := svc.ValidateMacaroon(ss.Context(),
|
err := svc.ValidateMacaroon(
|
||||||
permissionMap[info.FullMethod])
|
ss.Context(), permissionMap[info.FullMethod],
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
223
rpcserver.go
223
rpcserver.go
@ -4,11 +4,13 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"crypto/tls"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -24,17 +26,21 @@ import (
|
|||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
"github.com/coreos/bbolt"
|
"github.com/coreos/bbolt"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
proxy "github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||||
"github.com/lightningnetwork/lnd/build"
|
"github.com/lightningnetwork/lnd/build"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||||
|
"github.com/lightningnetwork/lnd/lncfg"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/lightningnetwork/lnd/macaroons"
|
||||||
"github.com/lightningnetwork/lnd/routing"
|
"github.com/lightningnetwork/lnd/routing"
|
||||||
"github.com/lightningnetwork/lnd/signal"
|
"github.com/lightningnetwork/lnd/signal"
|
||||||
"github.com/lightningnetwork/lnd/zpay32"
|
"github.com/lightningnetwork/lnd/zpay32"
|
||||||
"github.com/tv42/zbase32"
|
"github.com/tv42/zbase32"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
"gopkg.in/macaroon-bakery.v2/bakery"
|
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -339,6 +345,31 @@ type rpcServer struct {
|
|||||||
|
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
|
||||||
|
// subServers are a set of sub-RPC servers that use the same gRPC and
|
||||||
|
// listening sockets as the main RPC server, but which maintain their
|
||||||
|
// own independent service. This allows us to expose a set of
|
||||||
|
// micro-service like abstractions to the outside world for users to
|
||||||
|
// consume.
|
||||||
|
subServers []lnrpc.SubServer
|
||||||
|
|
||||||
|
// grpcServer is the main gRPC server that this RPC server, and all the
|
||||||
|
// sub-servers will use to register themselves and accept client
|
||||||
|
// requests from.
|
||||||
|
grpcServer *grpc.Server
|
||||||
|
|
||||||
|
// listenerCleanUp are a set of closures functions that will allow this
|
||||||
|
// main RPC server to clean up all the listening socket created for the
|
||||||
|
// server.
|
||||||
|
listenerCleanUp []func()
|
||||||
|
|
||||||
|
// restServerOpts are a set of gRPC dial options that the REST server
|
||||||
|
// proxy will use to connect to the main gRPC server.
|
||||||
|
restServerOpts []grpc.DialOption
|
||||||
|
|
||||||
|
// tlsCfg is the TLS config that allows the REST server proxy to
|
||||||
|
// connect to the main gRPC server to proxy all incoming requests.
|
||||||
|
tlsCfg *tls.Config
|
||||||
|
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,12 +377,106 @@ type rpcServer struct {
|
|||||||
// LightningServer gRPC service.
|
// LightningServer gRPC service.
|
||||||
var _ lnrpc.LightningServer = (*rpcServer)(nil)
|
var _ lnrpc.LightningServer = (*rpcServer)(nil)
|
||||||
|
|
||||||
// newRPCServer creates and returns a new instance of the rpcServer.
|
// newRPCServer creates and returns a new instance of the rpcServer. The
|
||||||
func newRPCServer(s *server) *rpcServer {
|
// rpcServer will handle creating all listening sockets needed by it, and any
|
||||||
return &rpcServer{
|
// of the sub-servers that it maintains. The set of serverOpts should be the
|
||||||
server: s,
|
// base level options passed to the grPC server. This typically includes things
|
||||||
quit: make(chan struct{}, 1),
|
// like requiring TLS, etc.
|
||||||
|
func newRPCServer(s *server, macService *macaroons.Service,
|
||||||
|
subServerCgs *subRPCServerConfigs, serverOpts []grpc.ServerOption,
|
||||||
|
restServerOpts []grpc.DialOption,
|
||||||
|
tlsCfg *tls.Config) (*rpcServer, error) {
|
||||||
|
|
||||||
|
var (
|
||||||
|
subServers []lnrpc.SubServer
|
||||||
|
subServerPerms []lnrpc.MacaroonPerms
|
||||||
|
)
|
||||||
|
|
||||||
|
// Before we create any of the sub-servers, we need to ensure that all
|
||||||
|
// the dependencies they need are properly populated within each sub
|
||||||
|
// server configuration struct.
|
||||||
|
err := subServerCgs.PopulateDependencies(
|
||||||
|
s.cc, networkDir, macService,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now that the sub-servers have all their dependencies in place, we
|
||||||
|
// can create each sub-server!
|
||||||
|
registeredSubServers := lnrpc.RegisteredSubServers()
|
||||||
|
for _, subServer := range registeredSubServers {
|
||||||
|
subServerInstance, macPerms, err := subServer.New(subServerCgs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll collect the sub-server, and also the set of
|
||||||
|
// permissions it needs for macaroons so we can apply the
|
||||||
|
// interceptors below.
|
||||||
|
subServers = append(subServers, subServerInstance)
|
||||||
|
subServerPerms = append(subServerPerms, macPerms)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, we need to merge the set of sub server macaroon permissions
|
||||||
|
// with the main RPC server permissions so we can unite them under a
|
||||||
|
// single set of interceptors.
|
||||||
|
for _, subServerPerm := range subServerPerms {
|
||||||
|
for method, ops := range subServerPerm {
|
||||||
|
// For each new method:ops combo, we also ensure that
|
||||||
|
// non of the sub-servers try to override each other.
|
||||||
|
if _, ok := permissions[method]; ok {
|
||||||
|
return nil, fmt.Errorf("detected duplicate "+
|
||||||
|
"macaroon constraints for path: %v",
|
||||||
|
method)
|
||||||
|
}
|
||||||
|
|
||||||
|
permissions[method] = ops
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If macaroons aren't disabled (a non-nil service), then we'll set up
|
||||||
|
// our set of interceptors which will allow us handle the macaroon
|
||||||
|
// authentication in a single location .
|
||||||
|
if macService != nil {
|
||||||
|
unaryInterceptor := grpc.UnaryInterceptor(
|
||||||
|
macService.UnaryServerInterceptor(permissions),
|
||||||
|
)
|
||||||
|
streamInterceptor := grpc.StreamInterceptor(
|
||||||
|
macService.StreamServerInterceptor(permissions),
|
||||||
|
)
|
||||||
|
|
||||||
|
serverOpts = append(serverOpts,
|
||||||
|
unaryInterceptor, streamInterceptor,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, with all the pre-set up complete, we can create the main
|
||||||
|
// gRPC server, and register the main lnrpc server along side.
|
||||||
|
grpcServer := grpc.NewServer(serverOpts...)
|
||||||
|
rootRPCServer := &rpcServer{
|
||||||
|
restServerOpts: restServerOpts,
|
||||||
|
subServers: subServers,
|
||||||
|
tlsCfg: tlsCfg,
|
||||||
|
grpcServer: grpcServer,
|
||||||
|
server: s,
|
||||||
|
quit: make(chan struct{}, 1),
|
||||||
|
}
|
||||||
|
lnrpc.RegisterLightningServer(grpcServer, rootRPCServer)
|
||||||
|
|
||||||
|
// Now the main RPC server has been registered, we'll iterate through
|
||||||
|
// all the sub-RPC servers and register them to ensure that requests
|
||||||
|
// are properly routed towards them.
|
||||||
|
for _, subServer := range subServers {
|
||||||
|
err := subServer.RegisterWithRootServer(grpcServer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to register "+
|
||||||
|
"sub-server %v with root: %v",
|
||||||
|
subServer.Name(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rootRPCServer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start launches any helper goroutines required for the rpcServer to function.
|
// Start launches any helper goroutines required for the rpcServer to function.
|
||||||
@ -360,6 +485,72 @@ func (r *rpcServer) Start() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First, we'll start all the sub-servers to ensure that they're ready
|
||||||
|
// to take new requests in.
|
||||||
|
//
|
||||||
|
// TODO(roasbeef): some may require that the entire daemon be started
|
||||||
|
// at that point
|
||||||
|
for _, subServer := range r.subServers {
|
||||||
|
rpcsLog.Debugf("Starting sub RPC server: %v", subServer.Name())
|
||||||
|
|
||||||
|
if err := subServer.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// With all the sub-servers started, we'll spin up the listeners for
|
||||||
|
// the main RPC server itself.
|
||||||
|
for _, listener := range cfg.RPCListeners {
|
||||||
|
lis, err := lncfg.ListenOnAddress(listener)
|
||||||
|
if err != nil {
|
||||||
|
ltndLog.Errorf(
|
||||||
|
"RPC server unable to listen on %s", listener,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.listenerCleanUp = append(r.listenerCleanUp, func() {
|
||||||
|
lis.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
rpcsLog.Infof("RPC server listening on %s", lis.Addr())
|
||||||
|
r.grpcServer.Serve(lis)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, start the REST proxy for our gRPC server above.
|
||||||
|
//
|
||||||
|
// TODO(roasbeef): eventually also allow the sub-servers to themselves
|
||||||
|
// have a REST proxy.
|
||||||
|
mux := proxy.NewServeMux()
|
||||||
|
err := lnrpc.RegisterLightningHandlerFromEndpoint(
|
||||||
|
context.Background(), mux, cfg.RPCListeners[0].String(),
|
||||||
|
r.restServerOpts,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, restEndpoint := range cfg.RESTListeners {
|
||||||
|
lis, err := lncfg.TLSListenOnAddress(restEndpoint, r.tlsCfg)
|
||||||
|
if err != nil {
|
||||||
|
ltndLog.Errorf(
|
||||||
|
"gRPC proxy unable to listen on %s",
|
||||||
|
restEndpoint,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.listenerCleanUp = append(r.listenerCleanUp, func() {
|
||||||
|
lis.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
rpcsLog.Infof("gRPC proxy started at %s", lis.Addr())
|
||||||
|
http.Serve(lis, mux)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,8 +560,30 @@ func (r *rpcServer) Stop() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpcsLog.Infof("Stopping RPC Server")
|
||||||
|
|
||||||
close(r.quit)
|
close(r.quit)
|
||||||
|
|
||||||
|
// After we've signalled all of our active goroutines to exit, we'll
|
||||||
|
// then do the same to signal a graceful shutdown of all the sub
|
||||||
|
// servers.
|
||||||
|
for _, subServer := range r.subServers {
|
||||||
|
rpcsLog.Infof("Stopping %v Sub-RPC Server",
|
||||||
|
subServer.Name())
|
||||||
|
|
||||||
|
if err := subServer.Stop(); err != nil {
|
||||||
|
rpcsLog.Errorf("unable to stop sub-server %v: %v",
|
||||||
|
subServer.Name(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, we can clean up all the listening sockets to ensure that we
|
||||||
|
// give the file descriptors back to the OS.
|
||||||
|
for _, cleanUp := range r.listenerCleanUp {
|
||||||
|
cleanUp()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
128
subrpcserver_config.go
Normal file
128
subrpcserver_config.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
|
||||||
|
"github.com/lightningnetwork/lnd/macaroons"
|
||||||
|
)
|
||||||
|
|
||||||
|
// subRPCServerConfigs is special sub-config in the main configuration that
|
||||||
|
// houses the configuration for the optional sub-servers. These sub-RPC servers
|
||||||
|
// are meant to house experimental new features that may eventually make it
|
||||||
|
// into the main RPC server that lnd exposes. Special methods are present on
|
||||||
|
// this struct to allow the main RPC server to create and manipulate the
|
||||||
|
// sub-RPC servers in a generalized manner.
|
||||||
|
type subRPCServerConfigs struct {
|
||||||
|
// SignRPC is a sub-RPC server that exposes signing of arbitrary inputs
|
||||||
|
// as a gRPC service.
|
||||||
|
SignRPC *signrpc.Config `group:"signrpc" namespace:"signrpc"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PopulateDependencies attempts to iterate through all the sub-server configs
|
||||||
|
// within this struct, and populate the items it requires based on the main
|
||||||
|
// configuration file, and the chain control.
|
||||||
|
//
|
||||||
|
// NOTE: This MUST be called before any callers are permitted to execute the
|
||||||
|
// FetchConfig method.
|
||||||
|
func (s *subRPCServerConfigs) PopulateDependencies(cc *chainControl,
|
||||||
|
networkDir string, macService *macaroons.Service) error {
|
||||||
|
|
||||||
|
// First, we'll use reflect to obtain a version of the config struct
|
||||||
|
// that allows us to programmatically inspect its fields.
|
||||||
|
selfVal := extractReflectValue(s)
|
||||||
|
selfType := selfVal.Type()
|
||||||
|
|
||||||
|
numFields := selfVal.NumField()
|
||||||
|
for i := 0; i < numFields; i++ {
|
||||||
|
field := selfVal.Field(i)
|
||||||
|
fieldElem := field.Elem()
|
||||||
|
fieldName := selfType.Field(i).Name
|
||||||
|
|
||||||
|
ltndLog.Debugf("Populating dependencies for sub RPC "+
|
||||||
|
"server: %v", fieldName)
|
||||||
|
|
||||||
|
// If this sub-config doesn't actually have any fields, then we
|
||||||
|
// can skip it, as the build tag for it is likely off.
|
||||||
|
if fieldElem.NumField() == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !fieldElem.CanSet() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch cfg := field.Interface().(type) {
|
||||||
|
case *signrpc.Config:
|
||||||
|
subCfgValue := extractReflectValue(cfg)
|
||||||
|
|
||||||
|
subCfgValue.FieldByName("MacService").Set(
|
||||||
|
reflect.ValueOf(macService),
|
||||||
|
)
|
||||||
|
subCfgValue.FieldByName("NetworkDir").Set(
|
||||||
|
reflect.ValueOf(networkDir),
|
||||||
|
)
|
||||||
|
subCfgValue.FieldByName("Signer").Set(
|
||||||
|
reflect.ValueOf(cc.signer),
|
||||||
|
)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown field: %v, %T", fieldName,
|
||||||
|
cfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchConfig attempts to locate an existing configuration file mapped to the
|
||||||
|
// target sub-server. If we're unable to find a config file matching the
|
||||||
|
// subServerName name, then false will be returned for the second parameter.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the lnrpc.SubServerConfigDispatcher interface.
|
||||||
|
func (s *subRPCServerConfigs) FetchConfig(subServerName string) (interface{}, bool) {
|
||||||
|
// First, we'll use reflect to obtain a version of the config struct
|
||||||
|
// that allows us to programmatically inspect its fields.
|
||||||
|
selfVal := extractReflectValue(s)
|
||||||
|
|
||||||
|
// Now that we have the value of the struct, we can check to see if it
|
||||||
|
// has an attribute with the same name as the subServerName.
|
||||||
|
configVal := selfVal.FieldByName(subServerName)
|
||||||
|
configValElem := configVal.Elem()
|
||||||
|
|
||||||
|
// We'll now ensure that this field actually exists in this value. If
|
||||||
|
// not, then we'll return false for the ok value to indicate to the
|
||||||
|
// caller that this field doesn't actually exist.
|
||||||
|
if !configVal.IsValid() {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a config of this type is found, it doesn't have any fields, then
|
||||||
|
// it's the same as if it wasn't present. This can happen if the build
|
||||||
|
// tag for the sub-server is inactive.
|
||||||
|
if configValElem.NumField() == 0 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this pint, we know that the field is actually present in the
|
||||||
|
// config struct, so we can return it directly.
|
||||||
|
return configVal.Interface(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractReflectValue attempts to extract the value from an interface using
|
||||||
|
// the reflect package. The resulting reflect.Value allows the caller to
|
||||||
|
// programmatically examine and manipulate the underlying value.
|
||||||
|
func extractReflectValue(instance interface{}) reflect.Value {
|
||||||
|
var val reflect.Value
|
||||||
|
|
||||||
|
// If the type of the instance is a pointer, then we need to deference
|
||||||
|
// the pointer one level to get its value. Otherwise, we can access the
|
||||||
|
// value directly.
|
||||||
|
if reflect.TypeOf(instance).Kind() == reflect.Ptr {
|
||||||
|
val = reflect.ValueOf(instance).Elem()
|
||||||
|
} else {
|
||||||
|
val = reflect.ValueOf(instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
return val
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user