Skip to content

Commit

Permalink
feat: add LDK logic to recover from channels backup
Browse files Browse the repository at this point in the history
  • Loading branch information
rolznz committed Sep 24, 2024
1 parent cde81ac commit 66733a2
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 20 deletions.
13 changes: 7 additions & 6 deletions events/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Event struct {
}

type ChannelBackupEvent struct {
NodeID string `json:"node_id"`
Channels []ChannelBackupInfo `json:"channels"`
Monitors []ChannelMonitorBackup `json:"monitors"`
}
Expand All @@ -31,10 +32,10 @@ type ChannelMonitorBackup struct {
}

type ChannelBackupInfo struct {
ChannelID string `json:"channel_id"`
NodeID string `json:"node_id"`
PeerID string `json:"peer_id"`
ChannelSize uint64 `json:"channel_size"`
FundingTxID string `json:"funding_tx_id"`
FundingTxVout uint32 `json:"funding_tx_vout"`
ChannelID string `json:"channel_id"`
PeerID string `json:"peer_id"`
PeerSocketAddress string `json:"peer_socket_address"`
ChannelSize uint64 `json:"channel_size"`
FundingTxID string `json:"funding_tx_id"`
FundingTxVout uint32 `json:"funding_tx_vout"`
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/breez/breez-sdk-go v0.5.2
github.com/elnosh/gonuts v0.2.0
github.com/getAlby/glalby-go v0.0.0-20240621192717-95673c864d59
github.com/getAlby/ldk-node-go v0.0.0-20240815144818-6fa575b0a3f5
github.com/getAlby/ldk-node-go v0.0.0-20240924080718-27f0fdd2a75d
github.com/go-gormigrate/gormigrate/v2 v2.1.2
github.com/labstack/echo/v4 v4.12.0
github.com/nbd-wtf/go-nostr v0.35.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ github.com/getAlby/glalby-go v0.0.0-20240621192717-95673c864d59 h1:fSqdXE9uKhLcO
github.com/getAlby/glalby-go v0.0.0-20240621192717-95673c864d59/go.mod h1:ViyJvjlvv0GCesTJ7mb3fBo4G+/qsujDAFN90xZ7a9U=
github.com/getAlby/ldk-node-go v0.0.0-20240815144818-6fa575b0a3f5 h1:FY32CuHXa86wwXfBl+vcscVpwQ8yKhBSaL4ZVhTgfi4=
github.com/getAlby/ldk-node-go v0.0.0-20240815144818-6fa575b0a3f5/go.mod h1:8BRjtKcz8E0RyYTPEbMS8VIdgredcGSLne8vHDtcRLg=
github.com/getAlby/ldk-node-go v0.0.0-20240924080718-27f0fdd2a75d h1:scWSwE01B4fKdWmAQClerzSu7Ao691J5HheHdVLI7Tc=
github.com/getAlby/ldk-node-go v0.0.0-20240924080718-27f0fdd2a75d/go.mod h1:8BRjtKcz8E0RyYTPEbMS8VIdgredcGSLne8vHDtcRLg=
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
Expand Down
74 changes: 64 additions & 10 deletions lnclient/ldk/ldk.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import (
"strings"
"time"

// "github.com/getAlby/ldk-node-go/ldk_node"
"github.com/getAlby/hub/ldk_node"
"github.com/getAlby/ldk-node-go/ldk_node"
// "github.com/getAlby/hub/ldk_node"

"encoding/hex"
"encoding/json"
Expand Down Expand Up @@ -49,7 +49,7 @@ type LDKService struct {

const resetRouterKey = "ResetRouter"

func NewLDKService(ctx context.Context, cfg config.Config, eventPublisher events.EventPublisher, mnemonic, workDir string, network string) (result lnclient.LNClient, err error) {
func NewLDKService(ctx context.Context, cfg config.Config, eventPublisher events.EventPublisher, mnemonic, workDir string, network string, channelBackup *events.ChannelBackupEvent, restoredFromSeed bool) (result lnclient.LNClient, err error) {
if mnemonic == "" || workDir == "" {
return nil, errors.New("one or more required LDK configuration are missing")
}
Expand Down Expand Up @@ -119,6 +119,24 @@ func NewLDKService(ctx context.Context, cfg config.Config, eventPublisher events
// The liquidity source below is not used because we do not use the native LDK-node LSPS2 API.
builder.SetLiquiditySourceLsps2("52.88.33.119:9735", lsp.OlympusLSP().Pubkey, nil)

// recover from backup
if channelBackup != nil {
// add backed up channel monitors to LDK DB
encodedMonitors := []ldk_node.KeyValue{}
for _, monitor := range channelBackup.Monitors {
value, err := hex.DecodeString(monitor.Value)
if err != nil {
logger.Logger.WithError(err).Error("Failed to decode LDK channel monitor hex")
continue
}
encodedMonitors = append(encodedMonitors, ldk_node.KeyValue{
Key: monitor.Key,
Value: value,
})
}
builder.RestoreEncodedChannelMonitors(encodedMonitors)
}

//builder.SetLogDirPath (filepath.Join(newpath, "./logs")); // missing?
node, err := builder.Build()

Expand Down Expand Up @@ -188,6 +206,30 @@ func NewLDKService(ctx context.Context, cfg config.Config, eventPublisher events
return nil, err
}

if restoredFromSeed {
// generate some onchain addresses in case there were funds on them (when importing from an existing seed)
// NOTE: this may not be enough. The user could click the get new address button to fetch more addresses
// (this will probably be improved with BDK 1.0 anyway)
func() {
for i := 0; i < 10; i++ {
ls.node.OnchainPayment().NewAddress()
}
}()
}

// recover from backup
if channelBackup != nil {
// peer with original peers from channels so that we can send closing channel messages
for _, channel := range channelBackup.Channels {
err := ls.node.Connect(channel.PeerID, channel.PeerSocketAddress, true)
if err != nil {
logger.Logger.WithField("peer_id", channel.PeerID).WithError(err).Error("failed to peer to node from channel backup")
}
}

node.ForceCloseAllChannelsWithoutBroadcastingTxn()
}

logger.Logger.WithFields(logrus.Fields{
"nodeId": nodeId,
"status": node.Status(),
Expand Down Expand Up @@ -225,7 +267,6 @@ func NewLDKService(ctx context.Context, cfg config.Config, eventPublisher events
}).Info("LDK node synced successfully")

// backup channels after successful startup
// TODO: consider removing after all hubs are updated past 1.9.1
ls.backupChannels()

if ls.network == "bitcoin" {
Expand Down Expand Up @@ -1422,6 +1463,7 @@ func (ls *LDKService) handleLdkEvent(event *ldk_node.Event) {

func (ls *LDKService) backupChannels() {
ldkChannels := ls.node.ListChannels()
ldkPeers := ls.node.ListPeers()
channels := make([]events.ChannelBackupInfo, 0, len(ldkChannels))
for _, ldkChannel := range ldkChannels {
var fundingTxId string
Expand All @@ -1431,13 +1473,24 @@ func (ls *LDKService) backupChannels() {
fundingTxVout = ldkChannel.FundingTxo.Vout
}

var peer *ldk_node.PeerDetails
for _, matchingPeer := range ldkPeers {
if matchingPeer.NodeId == ldkChannel.CounterpartyNodeId {
peer = &matchingPeer
}
}
if peer == nil {
logger.Logger.WithField("peer_id", ldkChannel.CounterpartyNodeId).Error("failed to find peer for channel")
continue
}

channels = append(channels, events.ChannelBackupInfo{
ChannelID: ldkChannel.ChannelId,
NodeID: ls.node.NodeId(),
PeerID: ldkChannel.CounterpartyNodeId,
ChannelSize: ldkChannel.ChannelValueSats,
FundingTxID: fundingTxId,
FundingTxVout: fundingTxVout,
ChannelID: ldkChannel.ChannelId,
PeerID: ldkChannel.CounterpartyNodeId,
PeerSocketAddress: peer.Address,
ChannelSize: ldkChannel.ChannelValueSats,
FundingTxID: fundingTxId,
FundingTxVout: fundingTxVout,
})
}

Expand All @@ -1458,6 +1511,7 @@ func (ls *LDKService) backupChannels() {
event := &events.ChannelBackupEvent{
Channels: channels,
Monitors: encodedMonitors,
NodeID: ls.node.NodeId(),
}

ls.saveStaticChannelBackupToDisk(event)
Expand Down
4 changes: 2 additions & 2 deletions lnclient/ldk/ldk_event_broadcaster.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"slices"
"time"

//"github.com/getAlby/ldk-node-go/ldk_node"
"github.com/getAlby/hub/ldk_node"
"github.com/getAlby/ldk-node-go/ldk_node"
// "github.com/getAlby/hub/ldk_node"
"github.com/getAlby/hub/logger"

"github.com/sirupsen/logrus"
Expand Down
2 changes: 1 addition & 1 deletion service/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func (svc *service) launchLNBackend(ctx context.Context, encryptionKey string) e
Mnemonic, _ := svc.cfg.Get("Mnemonic", encryptionKey)
LDKWorkdir := path.Join(svc.cfg.GetEnv().Workdir, "ldk")

lnClient, err = ldk.NewLDKService(ctx, svc.cfg, svc.eventPublisher, Mnemonic, LDKWorkdir, svc.cfg.GetEnv().LDKNetwork)
lnClient, err = ldk.NewLDKService(ctx, svc.cfg, svc.eventPublisher, Mnemonic, LDKWorkdir, svc.cfg.GetEnv().LDKNetwork, nil, false)
case config.GreenlightBackendType:
Mnemonic, _ := svc.cfg.Get("Mnemonic", encryptionKey)
GreenlightInviteCode, _ := svc.cfg.Get("GreenlightInviteCode", encryptionKey)
Expand Down

0 comments on commit 66733a2

Please sign in to comment.