chanbackup: refuse to start the SubSwapper if we can't read the SCB file

In this commit, we add an additional defense against starting up with an
invalid SCB state. With this commit, we'll now fail to start up if we're
unable to update or read the existing SCB state from disk. This will
prevent an lnd node from starting up with an invalid SCB file, an SCB
file it can't decrypt, or an SCB left over from another node.
This commit is contained in:
Olaoluwa Osuntokun 2020-06-15 17:41:44 -07:00
parent fc65c9b2cc
commit 496e29d02e
No known key found for this signature in database
GPG Key ID: BC13F65E2DC84465
2 changed files with 19 additions and 11 deletions

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"net" "net"
"os"
"sync" "sync"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
@ -134,13 +135,24 @@ func NewSubSwapper(startingChans []Single, chanNotifier ChannelNotifier,
// Start starts the chanbackup.SubSwapper. // Start starts the chanbackup.SubSwapper.
func (s *SubSwapper) Start() error { func (s *SubSwapper) Start() error {
var startErr error
s.started.Do(func() { s.started.Do(func() {
log.Infof("Starting chanbackup.SubSwapper") log.Infof("Starting chanbackup.SubSwapper")
// Before we enter our main loop, we'll update the on-disk
// state with the latest Single state, as nodes may have new
// advertised addresses.
if err := s.updateBackupFile(); err != nil {
startErr = fmt.Errorf("unable to refresh backup "+
"file: %v", err)
return
}
s.wg.Add(1) s.wg.Add(1)
go s.backupUpdater() go s.backupUpdater()
}) })
return nil
return startErr
} }
// Stop signals the SubSwapper to being a graceful shutdown. // Stop signals the SubSwapper to being a graceful shutdown.
@ -162,7 +174,11 @@ func (s *SubSwapper) updateBackupFile(closedChans ...wire.OutPoint) error {
// already have on-disk, to make sure we can decode it (proper seed) // already have on-disk, to make sure we can decode it (proper seed)
// and that we're able to combine it with our new data. // and that we're able to combine it with our new data.
diskMulti, err := s.Swapper.ExtractMulti(s.keyRing) diskMulti, err := s.Swapper.ExtractMulti(s.keyRing)
if err != nil {
// If the file doesn't exist on disk, then that's OK as it was never
// created. In this case we'll continue onwards as it isn't a critical
// error.
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("unable to extract on disk encrypted "+ return fmt.Errorf("unable to extract on disk encrypted "+
"SCB: %v", err) "SCB: %v", err)
} }
@ -235,14 +251,6 @@ func (s *SubSwapper) backupUpdater() {
log.Debugf("SubSwapper's backupUpdater is active!") log.Debugf("SubSwapper's backupUpdater is active!")
// Before we enter our main loop, we'll update the on-disk state with
// the latest Single state, as nodes may have new advertised addresses.
//
// TODO(roasbeef): do in Start() so we don't start with an invalid SCB?
if err := s.updateBackupFile(); err != nil {
log.Errorf("Unable to refresh backup file: %v", err)
}
for { for {
select { select {
// The channel state has been modified! We'll evaluate all // The channel state has been modified! We'll evaluate all

@ -21,7 +21,7 @@ type mockSwapper struct {
func newMockSwapper(keychain keychain.KeyRing) *mockSwapper { func newMockSwapper(keychain keychain.KeyRing) *mockSwapper {
return &mockSwapper{ return &mockSwapper{
swaps: make(chan PackedMulti), swaps: make(chan PackedMulti, 1),
keyChain: keychain, keyChain: keychain,
swapState: &Multi{}, swapState: &Multi{},
} }