multi: add force tick endpoint behind debug server

To itest our autolooper, we need to be able to trigger dispatch on
demand. This functionality is included in a separate rpc server behind
a dev flag. Since it is unlikely that we need to split loop into
multiple rpc servers, this commit simply adds an additional debug server
rather than opting for a full subserver setup.
pull/305/head
carla 4 years ago
parent 62be092c87
commit 87b02b7715
No known key found for this signature in database
GPG Key ID: 4CA7FE54A6213C91

@ -24,6 +24,7 @@ sudo: required
script: script:
- export GO111MODULE=on - export GO111MODULE=on
- make lint unit build mod-check - make lint unit build mod-check
- make tags=dev
after_script: after_script:
- echo "Uploading to termbin.com..." && find *.log | xargs -I{} sh -c "cat {} | nc termbin.com 9999 | xargs -r0 printf '{} uploaded to %s'" - echo "Uploading to termbin.com..." && find *.log | xargs -I{} sh -c "cat {} | nc termbin.com 9999 | xargs -r0 printf '{} uploaded to %s'"

@ -11,6 +11,7 @@ GOMOD := GO111MODULE=on go mod
COMMIT := $(shell git describe --abbrev=40 --dirty) COMMIT := $(shell git describe --abbrev=40 --dirty)
LDFLAGS := -ldflags "-X $(PKG)/build.Commit=$(COMMIT)" LDFLAGS := -ldflags "-X $(PKG)/build.Commit=$(COMMIT)"
DEV_TAGS = dev
GOFILES_NOVENDOR = $(shell find . -type f -name '*.go' -not -path "./vendor/*") GOFILES_NOVENDOR = $(shell find . -type f -name '*.go' -not -path "./vendor/*")
GOLIST := go list $(PKG)/... | grep -v '/vendor/' GOLIST := go list $(PKG)/... | grep -v '/vendor/'
@ -64,10 +65,10 @@ mod-check:
build: build:
@$(call print, "Building debug loop and loopd.") @$(call print, "Building debug loop and loopd.")
$(GOBUILD) -o loop-debug $(LDFLAGS) $(PKG)/cmd/loop $(GOBUILD) -tags="$(DEV_TAGS)" -o loop-debug $(LDFLAGS) $(PKG)/cmd/loop
$(GOBUILD) -o loopd-debug $(LDFLAGS) $(PKG)/cmd/loopd $(GOBUILD) -tags="$(DEV_TAGS)" -o loopd-debug $(LDFLAGS) $(PKG)/cmd/loopd
install: install:
@$(call print, "Installing loop and loopd.") @$(call print, "Installing loop and loopd.")
$(GOINSTALL) $(LDFLAGS) $(PKG)/cmd/loop $(GOINSTALL) -tags="${tags}" $(LDFLAGS) $(PKG)/cmd/loop
$(GOINSTALL) $(LDFLAGS) $(PKG)/cmd/loopd $(GOINSTALL) -tags="${tags}" $(LDFLAGS) $(PKG)/cmd/loopd

@ -453,6 +453,17 @@ func (m *Manager) autoloop(ctx context.Context) error {
return nil return nil
} }
// ForceAutoLoop force-ticks our auto-out ticker.
func (m *Manager) ForceAutoLoop(ctx context.Context) error {
select {
case m.cfg.AutoOutTicker.Force <- m.cfg.Clock.Now():
return nil
case <-ctx.Done():
return ctx.Err()
}
}
// SuggestSwaps returns a set of swap suggestions based on our current liquidity // SuggestSwaps returns a set of swap suggestions based on our current liquidity
// balance for the set of rules configured for the manager, failing if there are // balance for the set of rules configured for the manager, failing if there are
// no rules set. It takes an autoOut boolean that indicates whether the // no rules set. It takes an autoOut boolean that indicates whether the

@ -197,6 +197,9 @@ func (d *Daemon) startWebServers() error {
d.grpcServer = grpc.NewServer(serverOpts...) d.grpcServer = grpc.NewServer(serverOpts...)
looprpc.RegisterSwapClientServer(d.grpcServer, d) looprpc.RegisterSwapClientServer(d.grpcServer, d)
// Register our debug server if it is compiled in.
d.registerDebugServer()
// Next, start the gRPC server listening for HTTP/2 connections. // Next, start the gRPC server listening for HTTP/2 connections.
log.Infof("Starting gRPC listener") log.Infof("Starting gRPC listener")
serverTLSCfg, restClientCreds, err := getTLSConfig(d.cfg) serverTLSCfg, restClientCreds, err := getTLSConfig(d.cfg)
@ -355,6 +358,7 @@ func (d *Daemon) initialize() error {
// Now finally fully initialize the swap client RPC server instance. // Now finally fully initialize the swap client RPC server instance.
d.swapClientServer = swapClientServer{ d.swapClientServer = swapClientServer{
network: lndclient.Network(d.cfg.Network),
impl: swapclient, impl: swapclient,
liquidityMgr: getLiquidityManager(swapclient), liquidityMgr: getLiquidityManager(swapclient),
lnd: &d.lnd.LndServices, lnd: &d.lnd.LndServices,

@ -164,7 +164,8 @@ func (d *Daemon) startMacaroonService() error {
// We only generate one default macaroon that contains all // We only generate one default macaroon that contains all
// existing permissions (equivalent to the admin.macaroon in // existing permissions (equivalent to the admin.macaroon in
// lnd). Custom macaroons can be created through the bakery // lnd). Custom macaroons can be created through the bakery
// RPC. // RPC. Add our debug permissions if required.
allPermissions = append(allPermissions, debugPermissions...)
loopMac, err := d.macaroonService.Oven.NewMacaroon( loopMac, err := d.macaroonService.Oven.NewMacaroon(
ctx, bakery.LatestVersion, nil, allPermissions..., ctx, bakery.LatestVersion, nil, allPermissions...,
) )
@ -196,6 +197,12 @@ func (d *Daemon) stopMacaroonService() error {
// macaroonInterceptor creates gRPC server options with the macaroon security // macaroonInterceptor creates gRPC server options with the macaroon security
// interceptors. // interceptors.
func (d *Daemon) macaroonInterceptor() []grpc.ServerOption { func (d *Daemon) macaroonInterceptor() []grpc.ServerOption {
// Add our debug permissions to our main set of required permissions
// if compiled in.
for endpoint, perm := range debugRequiredPermissions {
RequiredPermissions[endpoint] = perm
}
unaryInterceptor := d.macaroonService.UnaryServerInterceptor( unaryInterceptor := d.macaroonService.UnaryServerInterceptor(
RequiredPermissions, RequiredPermissions,
) )

@ -0,0 +1,14 @@
// +build !dev
package loopd
import "gopkg.in/macaroon-bakery.v2/bakery"
var (
debugRequiredPermissions = map[string][]bakery.Op{}
debugPermissions []bakery.Op
)
// registerDebugServer is our default debug server registration function, which
// excludes debug functionality.
func (d *Daemon) registerDebugServer() {}

@ -34,6 +34,7 @@ const (
// swapClientServer implements the grpc service exposed by loopd. // swapClientServer implements the grpc service exposed by loopd.
type swapClientServer struct { type swapClientServer struct {
network lndclient.Network
impl *loop.Client impl *loop.Client
liquidityMgr *liquidity.Manager liquidityMgr *liquidity.Manager
lnd *lndclient.LndServices lnd *lndclient.LndServices

@ -0,0 +1,48 @@
// +build dev
package loopd
import (
"context"
"fmt"
"gopkg.in/macaroon-bakery.v2/bakery"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/looprpc"
)
var (
debugRequiredPermissions = map[string][]bakery.Op{
"/looprpc.Debug/ForceAutoLoop": {{
Entity: "debug",
Action: "write",
}},
}
debugPermissions = []bakery.Op{
{
Entity: "debug",
Action: "write",
},
}
)
// registerDebugServer registers the debug server.
func (d *Daemon) registerDebugServer() {
looprpc.RegisterDebugServer(d.grpcServer, d)
}
// ForceAutoLoop triggers our liquidity manager to dispatch an automated swap,
// if one is suggested. This endpoint is only for testing purposes and cannot be
// used on mainnet.
func (s *swapClientServer) ForceAutoLoop(ctx context.Context,
_ *looprpc.ForceAutoLoopRequest) (*looprpc.ForceAutoLoopResponse, error) {
if s.network == lndclient.NetworkMainnet {
return nil, fmt.Errorf("force autoloop not allowed on mainnet")
}
err := s.liquidityMgr.ForceAutoLoop(ctx)
return &looprpc.ForceAutoLoopResponse{}, err
}

@ -0,0 +1,194 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: debug.proto
package looprpc
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
// 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.ProtoPackageIsVersion3 // please upgrade the proto package
type ForceAutoLoopRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ForceAutoLoopRequest) Reset() { *m = ForceAutoLoopRequest{} }
func (m *ForceAutoLoopRequest) String() string { return proto.CompactTextString(m) }
func (*ForceAutoLoopRequest) ProtoMessage() {}
func (*ForceAutoLoopRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_8d9d361be58531fb, []int{0}
}
func (m *ForceAutoLoopRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ForceAutoLoopRequest.Unmarshal(m, b)
}
func (m *ForceAutoLoopRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ForceAutoLoopRequest.Marshal(b, m, deterministic)
}
func (m *ForceAutoLoopRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ForceAutoLoopRequest.Merge(m, src)
}
func (m *ForceAutoLoopRequest) XXX_Size() int {
return xxx_messageInfo_ForceAutoLoopRequest.Size(m)
}
func (m *ForceAutoLoopRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ForceAutoLoopRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ForceAutoLoopRequest proto.InternalMessageInfo
type ForceAutoLoopResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ForceAutoLoopResponse) Reset() { *m = ForceAutoLoopResponse{} }
func (m *ForceAutoLoopResponse) String() string { return proto.CompactTextString(m) }
func (*ForceAutoLoopResponse) ProtoMessage() {}
func (*ForceAutoLoopResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_8d9d361be58531fb, []int{1}
}
func (m *ForceAutoLoopResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ForceAutoLoopResponse.Unmarshal(m, b)
}
func (m *ForceAutoLoopResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ForceAutoLoopResponse.Marshal(b, m, deterministic)
}
func (m *ForceAutoLoopResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ForceAutoLoopResponse.Merge(m, src)
}
func (m *ForceAutoLoopResponse) XXX_Size() int {
return xxx_messageInfo_ForceAutoLoopResponse.Size(m)
}
func (m *ForceAutoLoopResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ForceAutoLoopResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ForceAutoLoopResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*ForceAutoLoopRequest)(nil), "looprpc.ForceAutoLoopRequest")
proto.RegisterType((*ForceAutoLoopResponse)(nil), "looprpc.ForceAutoLoopResponse")
}
func init() { proto.RegisterFile("debug.proto", fileDescriptor_8d9d361be58531fb) }
var fileDescriptor_8d9d361be58531fb = []byte{
// 117 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4e, 0x49, 0x4d, 0x2a,
0x4d, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xcf, 0xc9, 0xcf, 0x2f, 0x28, 0x2a, 0x48,
0x56, 0x12, 0xe3, 0x12, 0x71, 0xcb, 0x2f, 0x4a, 0x4e, 0x75, 0x2c, 0x2d, 0xc9, 0xf7, 0xc9, 0xcf,
0x2f, 0x08, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x51, 0x12, 0xe7, 0x12, 0x45, 0x13, 0x2f, 0x2e,
0xc8, 0xcf, 0x2b, 0x4e, 0x35, 0x8a, 0xe4, 0x62, 0x75, 0x01, 0x19, 0x24, 0x14, 0xc0, 0xc5, 0x8b,
0xa2, 0x42, 0x48, 0x56, 0x0f, 0x6a, 0xa8, 0x1e, 0x36, 0x13, 0xa5, 0xe4, 0x70, 0x49, 0x43, 0x0c,
0x56, 0x62, 0x48, 0x62, 0x03, 0xbb, 0xcd, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x39, 0x7a, 0x3c,
0x6b, 0xaa, 0x00, 0x00, 0x00,
}
// 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
// DebugClient is the client API for Debug service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type DebugClient interface {
//
//ForceAutoLoop is intended for *testing purposes only* and will not work on
//mainnet. This endpoint ticks our autoloop timer, triggering automated
//dispatch of a swap if one is suggested.
ForceAutoLoop(ctx context.Context, in *ForceAutoLoopRequest, opts ...grpc.CallOption) (*ForceAutoLoopResponse, error)
}
type debugClient struct {
cc *grpc.ClientConn
}
func NewDebugClient(cc *grpc.ClientConn) DebugClient {
return &debugClient{cc}
}
func (c *debugClient) ForceAutoLoop(ctx context.Context, in *ForceAutoLoopRequest, opts ...grpc.CallOption) (*ForceAutoLoopResponse, error) {
out := new(ForceAutoLoopResponse)
err := c.cc.Invoke(ctx, "/looprpc.Debug/ForceAutoLoop", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// DebugServer is the server API for Debug service.
type DebugServer interface {
//
//ForceAutoLoop is intended for *testing purposes only* and will not work on
//mainnet. This endpoint ticks our autoloop timer, triggering automated
//dispatch of a swap if one is suggested.
ForceAutoLoop(context.Context, *ForceAutoLoopRequest) (*ForceAutoLoopResponse, error)
}
// UnimplementedDebugServer can be embedded to have forward compatible implementations.
type UnimplementedDebugServer struct {
}
func (*UnimplementedDebugServer) ForceAutoLoop(ctx context.Context, req *ForceAutoLoopRequest) (*ForceAutoLoopResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ForceAutoLoop not implemented")
}
func RegisterDebugServer(s *grpc.Server, srv DebugServer) {
s.RegisterService(&_Debug_serviceDesc, srv)
}
func _Debug_ForceAutoLoop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ForceAutoLoopRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(DebugServer).ForceAutoLoop(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/looprpc.Debug/ForceAutoLoop",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DebugServer).ForceAutoLoop(ctx, req.(*ForceAutoLoopRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Debug_serviceDesc = grpc.ServiceDesc{
ServiceName: "looprpc.Debug",
HandlerType: (*DebugServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ForceAutoLoop",
Handler: _Debug_ForceAutoLoop_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "debug.proto",
}

@ -0,0 +1,23 @@
syntax = "proto3";
package looprpc;
/*
Debug is a service that exposes endpoints intended for testing purposes. These
endpoints should not operate on mainnet, and should only be included if loop is
built with the dev build tag.
*/
service Debug {
/*
ForceAutoLoop is intended for *testing purposes only* and will not work on
mainnet. This endpoint ticks our autoloop timer, triggering automated
dispatch of a swap if one is suggested.
*/
rpc ForceAutoLoop(ForceAutoLoopRequest) returns (ForceAutoLoopResponse){}
}
message ForceAutoLoopRequest {
}
message ForceAutoLoopResponse {
}
Loading…
Cancel
Save