circuitbreaker/process.go

566 lines
12 KiB
Go
Raw Normal View History

2020-09-21 08:44:40 +02:00
package main
import (
"context"
2023-01-03 13:01:26 +01:00
"errors"
2022-11-29 11:42:18 +01:00
"fmt"
2020-09-21 08:44:40 +02:00
"time"
"github.com/lightningnetwork/lnd/lnwire"
2020-09-21 08:44:40 +02:00
"github.com/lightningnetwork/lnd/routing/route"
2023-01-03 13:01:26 +01:00
"go.uber.org/zap"
2022-11-29 11:42:18 +01:00
"golang.org/x/sync/errgroup"
2020-09-21 08:44:40 +02:00
)
var (
2023-02-03 13:10:57 +01:00
rpcTimeout = 10 * time.Second
defaultPeerRefreshInterval = 10 * time.Minute
errChannelNotFound = errors.New("channel not found")
2020-09-21 08:44:40 +02:00
)
2023-01-03 13:01:26 +01:00
const burstSize = 10
2020-10-13 16:56:51 +02:00
type lndclient interface {
2023-02-06 17:01:26 +01:00
getInfo() (*info, error)
2020-10-13 16:56:51 +02:00
listChannels() (map[uint64]*channel, error)
2020-10-13 16:56:51 +02:00
listClosedChannels() (map[uint64]*channel, error)
2020-10-13 16:56:51 +02:00
getNodeAlias(key route.Vertex) (string, error)
2023-01-03 13:01:26 +01:00
subscribeHtlcEvents(ctx context.Context) (htlcEventsClient, error)
2020-10-13 16:56:51 +02:00
2023-01-03 13:01:26 +01:00
htlcInterceptor(ctx context.Context) (htlcInterceptorClient, error)
2022-11-29 12:03:36 +01:00
2022-12-31 09:40:33 +01:00
getPendingIncomingHtlcs(ctx context.Context, peer *route.Vertex) (
map[route.Vertex]map[circuitKey]*inFlightHtlc, error)
2020-10-13 16:56:51 +02:00
}
2020-09-21 08:44:40 +02:00
type circuitKey struct {
channel uint64
htlc uint64
}
type interceptEvent struct {
circuitKey
incomingMsat lnwire.MilliSatoshi
outgoingMsat lnwire.MilliSatoshi
resume func(bool) error
2023-01-03 13:01:26 +01:00
}
type resolvedEvent struct {
incomingCircuitKey circuitKey
outgoingCircuitKey circuitKey
settled bool
timestamp time.Time
2023-01-03 13:01:26 +01:00
}
type rateCounters struct {
counters map[route.Vertex]*peerState
}
type rateCountersRequest struct {
counters chan *rateCounters
2020-09-21 08:44:40 +02:00
}
type process struct {
2023-08-31 11:50:44 +02:00
db *Db
2020-10-13 16:56:51 +02:00
client lndclient
2023-01-03 13:01:26 +01:00
limits *Limits
log *zap.SugaredLogger
2020-09-21 08:44:40 +02:00
2023-01-03 13:01:26 +01:00
interceptChan chan interceptEvent
resolveChan chan resolvedEvent
updateLimitChan chan updateLimitEvent
rateCountersRequestChan chan rateCountersRequest
2023-02-03 13:10:57 +01:00
newPeerChan chan route.Vertex
2020-09-21 08:44:40 +02:00
identity route.Vertex
chanMap map[uint64]*channel
aliasMap map[route.Vertex]string
2021-09-11 13:51:44 +02:00
2022-11-29 15:18:13 +01:00
peerCtrls map[route.Vertex]*peerController
2022-10-13 13:55:44 +02:00
2023-02-03 13:10:57 +01:00
burstSize int
peerRefreshInterval time.Duration
2023-01-03 13:01:26 +01:00
2022-10-13 13:55:44 +02:00
// Testing hook
resolvedCallback func()
2020-09-21 08:44:40 +02:00
}
2023-08-31 11:50:44 +02:00
func NewProcess(client lndclient, log *zap.SugaredLogger, limits *Limits, db *Db) *process {
2020-09-21 08:44:40 +02:00
return &process{
2023-08-31 11:50:44 +02:00
db: db,
2023-01-03 13:01:26 +01:00
log: log,
client: client,
interceptChan: make(chan interceptEvent),
resolveChan: make(chan resolvedEvent),
updateLimitChan: make(chan updateLimitEvent),
rateCountersRequestChan: make(chan rateCountersRequest),
2023-02-03 13:10:57 +01:00
newPeerChan: make(chan route.Vertex),
2023-01-03 13:01:26 +01:00
chanMap: make(map[uint64]*channel),
aliasMap: make(map[route.Vertex]string),
peerCtrls: make(map[route.Vertex]*peerController),
limits: limits,
burstSize: burstSize,
2023-02-03 13:10:57 +01:00
peerRefreshInterval: defaultPeerRefreshInterval,
2020-09-21 08:44:40 +02:00
}
}
2023-01-03 13:01:26 +01:00
type updateLimitEvent struct {
limit *Limit
peer *route.Vertex
}
func (p *process) UpdateLimit(ctx context.Context, peer *route.Vertex,
limit *Limit) error {
if peer == nil && limit == nil {
return errors.New("cannot clear default limit")
}
update := updateLimitEvent{
limit: limit,
peer: peer,
}
select {
case p.updateLimitChan <- update:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
func (p *process) Run(ctx context.Context) error {
p.log.Info("CircuitBreaker started")
2020-09-21 08:44:40 +02:00
2023-02-06 17:01:26 +01:00
info, err := p.client.getInfo()
2020-09-21 08:44:40 +02:00
if err != nil {
return err
}
2023-02-06 17:01:26 +01:00
p.identity = info.nodeKey
2020-09-21 08:44:40 +02:00
2023-01-03 13:01:26 +01:00
p.log.Infow("Connected to lnd node",
2020-10-13 16:56:51 +02:00
"pubkey", p.identity.String())
2020-09-21 08:44:40 +02:00
2022-11-29 11:42:18 +01:00
group, ctx := errgroup.WithContext(ctx)
2023-01-03 13:01:26 +01:00
stream, err := p.client.subscribeHtlcEvents(ctx)
2020-09-21 08:44:40 +02:00
if err != nil {
return err
}
2022-11-29 11:42:18 +01:00
interceptor, err := p.client.htlcInterceptor(ctx)
2020-09-21 08:44:40 +02:00
if err != nil {
return err
}
2023-01-03 13:01:26 +01:00
p.log.Info("Interceptor/notification handlers registered")
2020-09-21 08:44:40 +02:00
2022-11-29 11:42:18 +01:00
group.Go(func() error {
err := p.processHtlcEvents(ctx, stream)
2020-09-21 08:44:40 +02:00
if err != nil {
2022-11-29 11:42:18 +01:00
return fmt.Errorf("htlc events error: %w", err)
2020-09-21 08:44:40 +02:00
}
2022-11-29 11:42:18 +01:00
return nil
})
group.Go(func() error {
err := p.processInterceptor(ctx, interceptor)
2020-09-21 08:44:40 +02:00
if err != nil {
2022-11-29 11:42:18 +01:00
return fmt.Errorf("interceptor error: %w", err)
2020-09-21 08:44:40 +02:00
}
2022-11-29 11:42:18 +01:00
return err
})
2023-02-03 13:10:57 +01:00
group.Go(func() error {
return p.peerRefreshLoop(ctx)
})
2022-11-29 11:42:18 +01:00
group.Go(func() error {
return p.runEventLoop(ctx)
2022-11-29 11:42:18 +01:00
})
return group.Wait()
2020-10-13 16:56:51 +02:00
}
2023-02-03 13:10:57 +01:00
func (p *process) peerRefreshLoop(ctx context.Context) error {
notifiedPeers := make(map[route.Vertex]struct{})
for {
// Get all peers.
channels, err := p.client.listChannels()
if err != nil {
return err
}
// Notify the main event loop of the new ones.
for _, ch := range channels {
if _, ok := notifiedPeers[ch.peer]; ok {
continue
}
notifiedPeers[ch.peer] = struct{}{}
select {
case p.newPeerChan <- ch.peer:
case <-ctx.Done():
return ctx.Err()
}
}
// Poll delay.
select {
case <-time.After(p.peerRefreshInterval):
case <-ctx.Done():
return ctx.Err()
}
}
}
2022-11-29 17:44:55 +01:00
func (p *process) getPeerController(ctx context.Context, peer route.Vertex,
startGo func(func() error)) *peerController {
2022-11-29 15:18:13 +01:00
ctrl, ok := p.peerCtrls[peer]
if ok {
return ctrl
}
// If the peer does not yet exist, initialize it with no pending htlcs.
htlcs := make(map[circuitKey]*inFlightHtlc)
2022-11-29 15:18:13 +01:00
2022-11-29 17:44:55 +01:00
return p.createPeerController(ctx, peer, startGo, htlcs)
2020-10-13 16:56:51 +02:00
}
2022-11-29 17:44:55 +01:00
func (p *process) createPeerController(ctx context.Context, peer route.Vertex,
startGo func(func() error),
htlcs map[circuitKey]*inFlightHtlc) *peerController {
2021-09-11 13:51:44 +02:00
2023-01-03 13:01:26 +01:00
peerCfg, ok := p.limits.PerPeer[peer]
if !ok {
peerCfg = p.limits.Default
}
2021-09-11 13:51:44 +02:00
2022-12-31 09:40:33 +01:00
cfg := &peerControllerCfg{
2023-01-03 13:01:26 +01:00
logger: p.log,
limit: peerCfg,
burstSize: p.burstSize,
htlcs: htlcs,
lnd: p.client,
pubKey: peer,
now: time.Now,
2023-08-31 11:50:44 +02:00
htlcCompleted: func(ctx context.Context, htlc *HtlcInfo) error {
// If the add time of a htlc is zero, it was resumed after a LND
// restart. We don't store these htlcs because they have
// incomplete information (missing add time and amounts).
if htlc.addTime.IsZero() {
2023-11-23 15:28:04 +01:00
log.Debugf("Not storing incomplete htlc resumed after "+
2023-08-31 11:50:44 +02:00
"restart: %v (%v) -> %v (%v)",
htlc.incomingCircuit.channel,
htlc.incomingCircuit.htlc,
htlc.outgoingCircuit.channel,
htlc.outgoingCircuit.htlc)
return nil
}
return p.db.RecordHtlcResolution(ctx, htlc)
},
2022-12-31 09:40:33 +01:00
}
ctrl := newPeerController(cfg)
2022-11-29 17:44:55 +01:00
startGo(func() error {
return ctrl.run(ctx)
})
2022-11-29 15:18:13 +01:00
p.peerCtrls[peer] = ctrl
return ctrl
2021-09-11 13:51:44 +02:00
}
func (p *process) runEventLoop(ctx context.Context) error {
2022-11-29 17:44:55 +01:00
group, ctx := errgroup.WithContext(ctx)
// Event loop will spin up new goroutines using the group that is passed in here.
// We run it in the same group so that both errors in eventLoop and those in
// the goroutines that is spins will will prompt exit.
group.Go(func() error {
return p.eventLoop(ctx, group)
})
return group.Wait()
}
func (p *process) eventLoop(ctx context.Context, group *errgroup.Group) error {
2022-11-29 15:18:13 +01:00
// Retrieve all pending htlcs from lnd.
2023-01-03 13:01:26 +01:00
htlcsPerPeer, err := p.client.getPendingIncomingHtlcs(ctx, nil)
2022-11-29 12:03:36 +01:00
if err != nil {
return err
}
2022-11-29 15:18:13 +01:00
// Initialize peer controllers with currently pending htlcs.
for peer, htlcs := range htlcsPerPeer {
2022-11-29 17:44:55 +01:00
p.createPeerController(ctx, peer, group.Go, htlcs)
2022-11-29 12:03:36 +01:00
}
2020-09-21 08:44:40 +02:00
for {
select {
case interceptEvent := <-p.interceptChan:
chanInfo, err := p.getChanInfo(interceptEvent.channel)
2020-09-21 08:44:40 +02:00
if err != nil {
return err
}
2022-11-29 17:44:55 +01:00
ctrl := p.getPeerController(ctx, chanInfo.peer, group.Go)
2020-09-21 08:44:40 +02:00
2022-11-29 17:44:55 +01:00
peerEvent := peerInterceptEvent{
interceptEvent: interceptEvent,
peerInitiated: !chanInfo.initiator,
}
if err := ctrl.process(ctx, peerEvent); err != nil {
2022-11-29 12:58:42 +01:00
return err
}
2020-09-21 08:44:40 +02:00
2023-01-03 13:01:26 +01:00
case resolvedEvent := <-p.resolveChan:
chanInfo, err := p.getChanInfo(
resolvedEvent.incomingCircuitKey.channel,
)
2020-09-21 08:44:40 +02:00
if err != nil {
return err
}
2022-11-29 17:44:55 +01:00
ctrl := p.getPeerController(ctx, chanInfo.peer, group.Go)
2020-10-13 16:56:51 +02:00
// Lookup the outgoing peer to supplement the information on the
// resolved event. Here we handle a channel lookup error
// differently to the incoming channel, because it's possible
// we were forwarded a HTLC with a bogus outgoing channel. If
// this is the case, LND would have failed the HTLC back even if
// we let it through. We catch and log that error, rather than
// exiting like we do with incoming channels (where we reasonably
// expect to find the channel). We still enforce channel lookup
// for successful HTLCs, because then we know that the channel
// does exist and should be found.
var outgoingPeer *route.Vertex
chanInfo, err = p.getChanInfo(
resolvedEvent.outgoingCircuitKey.channel,
)
switch {
case errors.Is(err, errChannelNotFound) && !resolvedEvent.settled:
log.Debugf("Channel not found for failed htlc: %v",
resolvedEvent.outgoingCircuitKey.channel)
case err != nil:
return err
default:
outgoingPeer = &chanInfo.peer
}
if err := ctrl.resolved(ctx, peerResolvedEvent{
resolvedEvent: resolvedEvent,
outgoingPeer: outgoingPeer,
}); err != nil {
2022-11-29 17:44:55 +01:00
return err
}
2020-10-13 16:56:51 +02:00
2022-10-13 13:55:44 +02:00
if p.resolvedCallback != nil {
p.resolvedCallback()
}
2023-01-03 13:01:26 +01:00
case update := <-p.updateLimitChan:
switch {
// Update sets default limit.
case update.peer == nil:
p.limits.Default = *update.limit
// Update all controllers that have no specific limit.
for node, ctrl := range p.peerCtrls {
_, ok := p.limits.PerPeer[node]
if ok {
continue
}
err := ctrl.updateLimit(ctx, *update.limit)
if err != nil {
return err
}
}
// Update sets specific limit.
case update.limit != nil:
p.limits.PerPeer[*update.peer] = *update.limit
// Update specific controller if it exists.
ctrl, ok := p.peerCtrls[*update.peer]
if ok {
err := ctrl.updateLimit(ctx, *update.limit)
if err != nil {
return err
}
}
// Update clears limit.
case update.limit == nil:
delete(p.limits.PerPeer, *update.peer)
// Apply default limit to peer controller.
ctrl, ok := p.peerCtrls[*update.peer]
if ok {
err := ctrl.updateLimit(ctx, p.limits.Default)
if err != nil {
return err
}
}
}
case req := <-p.rateCountersRequestChan:
allCounts := make(map[route.Vertex]*peerState)
for node, ctrl := range p.peerCtrls {
state, err := ctrl.state(ctx)
if err != nil {
return err
}
allCounts[node] = state
}
req.counters <- &rateCounters{
counters: allCounts,
}
2020-10-13 16:56:51 +02:00
case <-ctx.Done():
2022-11-29 11:42:18 +01:00
return ctx.Err()
2023-02-03 13:10:57 +01:00
// A new or existing peer has been reported.
case newPeer := <-p.newPeerChan:
p.log.Infow("New peer notification received", "peer", newPeer)
// Try to get the existing peer controller. If it doesn't exist, it
// will be created. This causes the peer to be reported over grpc.
_ = p.getPeerController(ctx, newPeer, group.Go)
2020-09-21 08:44:40 +02:00
}
}
}
2023-01-03 13:01:26 +01:00
func (p *process) getRateCounters(ctx context.Context) (
map[route.Vertex]*peerState, error) {
replyChan := make(chan *rateCounters)
select {
case p.rateCountersRequestChan <- rateCountersRequest{
counters: replyChan,
}:
case <-ctx.Done():
return nil, ctx.Err()
}
select {
case reply := <-replyChan:
return reply.counters, nil
case <-ctx.Done():
return nil, ctx.Err()
}
}
2022-11-29 11:42:18 +01:00
func (p *process) processHtlcEvents(ctx context.Context,
2023-01-03 13:01:26 +01:00
stream htlcEventsClient) error {
2022-11-29 11:42:18 +01:00
2020-09-21 08:44:40 +02:00
for {
2023-01-03 13:01:26 +01:00
event, err := stream.recv()
2020-09-21 08:44:40 +02:00
if err != nil {
return err
}
2022-11-29 11:42:18 +01:00
select {
2023-01-03 13:01:26 +01:00
case p.resolveChan <- *event:
2022-11-29 11:42:18 +01:00
case <-ctx.Done():
return ctx.Err()
2020-09-21 08:44:40 +02:00
}
}
}
2022-11-29 11:42:18 +01:00
func (p *process) processInterceptor(ctx context.Context,
2023-01-03 13:01:26 +01:00
interceptor htlcInterceptorClient) error {
2022-11-29 11:42:18 +01:00
2020-09-21 08:44:40 +02:00
for {
2023-01-03 13:01:26 +01:00
event, err := interceptor.recv()
2020-09-21 08:44:40 +02:00
if err != nil {
return err
}
2023-01-03 13:01:26 +01:00
key := event.circuitKey
2022-11-29 12:58:42 +01:00
resume := func(resume bool) error {
2023-01-03 13:01:26 +01:00
return interceptor.send(&interceptResponse{
key: key,
resume: resume,
})
2022-11-29 12:58:42 +01:00
}
2020-09-21 08:44:40 +02:00
2022-11-29 11:42:18 +01:00
select {
case p.interceptChan <- interceptEvent{
circuitKey: key,
incomingMsat: event.incomingMsat,
outgoingMsat: event.outgoingMsat,
resume: resume,
2022-11-29 11:42:18 +01:00
}:
case <-ctx.Done():
return ctx.Err()
2020-09-21 08:44:40 +02:00
}
}
}
func (p *process) getChanInfo(channel uint64) (*channel, error) {
// Try to look up from the cache.
ch, ok := p.chanMap[channel]
2020-09-21 08:44:40 +02:00
if ok {
return ch, nil
2020-09-21 08:44:40 +02:00
}
// Cache miss. Retrieve all channels and update the cache.
channels, err := p.client.listChannels()
2020-09-21 08:44:40 +02:00
if err != nil {
return nil, err
2020-09-21 08:44:40 +02:00
}
for chanId, ch := range channels {
p.chanMap[chanId] = ch
2020-09-21 08:44:40 +02:00
}
// Try looking up the channel again.
ch, ok = p.chanMap[channel]
if ok {
return ch, nil
}
2020-09-21 08:44:40 +02:00
// If the channel is not open, fall back to checking our closed
// channels.
closedChannels, err := p.client.listClosedChannels()
if err != nil {
return nil, err
}
// Add to cache and try again.
for chanId, ch := range closedChannels {
p.chanMap[chanId] = ch
}
ch, ok = p.chanMap[channel]
if ok {
return ch, nil
}
// Channel not found.
return nil, fmt.Errorf("%w: %v", errChannelNotFound, channel)
2020-09-21 08:44:40 +02:00
}