sweep: use chain notifier instead of chain IO for best block

Because the BestBlock method of ChainIO is not exposed through any
RPC we want to get rid of it so we can use the sweeper outside of
lnd too. Since the chain notifier now also delivers the current best
block we don't need the BestBlock method any more.
This commit is contained in:
Oliver Gugger 2019-10-07 13:21:25 +02:00
parent b6dda143d0
commit 8e4a897a60
No known key found for this signature in database
GPG Key ID: 8E4256593F177720
4 changed files with 38 additions and 58 deletions

@ -780,7 +780,6 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
return time.NewTimer(sweep.DefaultBatchWindowDuration).C return time.NewTimer(sweep.DefaultBatchWindowDuration).C
}, },
Notifier: cc.chainNotifier, Notifier: cc.chainNotifier,
ChainIO: cc.chainIO,
Store: sweeperStore, Store: sweeperStore,
MaxInputsPerTx: sweep.DefaultMaxInputsPerTx, MaxInputsPerTx: sweep.DefaultMaxInputsPerTx,
MaxSweepAttempts: sweep.DefaultMaxSweepAttempts, MaxSweepAttempts: sweep.DefaultMaxSweepAttempts,

@ -212,9 +212,6 @@ type UtxoSweeperConfig struct {
// certain on-chain events. // certain on-chain events.
Notifier chainntnfs.ChainNotifier Notifier chainntnfs.ChainNotifier
// ChainIO is used to determine the current block height.
ChainIO lnwallet.BlockChainIO
// Store stores the published sweeper txes. // Store stores the published sweeper txes.
Store SweeperStore Store SweeperStore
@ -323,20 +320,10 @@ func (s *UtxoSweeper) Start() error {
// not change from here on. // not change from here on.
s.relayFeeRate = s.cfg.FeeEstimator.RelayFeePerKW() s.relayFeeRate = s.cfg.FeeEstimator.RelayFeePerKW()
// Register for block epochs to retry sweeping every block. // We need to register for block epochs and retry sweeping every block.
bestHash, bestHeight, err := s.cfg.ChainIO.GetBestBlock() // We should get a notification with the current best block immediately
if err != nil { // if we don't provide any epoch. We'll wait for that in the collector.
return fmt.Errorf("get best block: %v", err) blockEpochs, err := s.cfg.Notifier.RegisterBlockEpochNtfn(nil)
}
log.Debugf("Best height: %v", bestHeight)
blockEpochs, err := s.cfg.Notifier.RegisterBlockEpochNtfn(
&chainntnfs.BlockEpoch{
Height: bestHeight,
Hash: bestHash,
},
)
if err != nil { if err != nil {
return fmt.Errorf("register block epoch ntfn: %v", err) return fmt.Errorf("register block epoch ntfn: %v", err)
} }
@ -347,10 +334,7 @@ func (s *UtxoSweeper) Start() error {
defer blockEpochs.Cancel() defer blockEpochs.Cancel()
defer s.wg.Done() defer s.wg.Done()
err := s.collector(blockEpochs.Epochs, bestHeight) s.collector(blockEpochs.Epochs)
if err != nil {
log.Errorf("sweeper stopped: %v", err)
}
}() }()
return nil return nil
@ -445,8 +429,18 @@ func (s *UtxoSweeper) feeRateForPreference(
// collector is the sweeper main loop. It processes new inputs, spend // collector is the sweeper main loop. It processes new inputs, spend
// notifications and counts down to publication of the sweep tx. // notifications and counts down to publication of the sweep tx.
func (s *UtxoSweeper) collector(blockEpochs <-chan *chainntnfs.BlockEpoch, func (s *UtxoSweeper) collector(blockEpochs <-chan *chainntnfs.BlockEpoch) {
bestHeight int32) error { // We registered for the block epochs with a nil request. The notifier
// should send us the current best block immediately. So we need to wait
// for it here because we need to know the current best height.
var bestHeight int32
select {
case bestBlock := <-blockEpochs:
bestHeight = bestBlock.Height
case <-s.quit:
return
}
for { for {
select { select {
@ -622,7 +616,7 @@ func (s *UtxoSweeper) collector(blockEpochs <-chan *chainntnfs.BlockEpoch,
// sweep. // sweep.
case epoch, ok := <-blockEpochs: case epoch, ok := <-blockEpochs:
if !ok { if !ok {
return nil return
} }
bestHeight = epoch.Height bestHeight = epoch.Height
@ -635,7 +629,7 @@ func (s *UtxoSweeper) collector(blockEpochs <-chan *chainntnfs.BlockEpoch,
} }
case <-s.quit: case <-s.quit:
return nil return
} }
} }
} }

@ -132,7 +132,6 @@ func createSweeperTestContext(t *testing.T) *sweeperTestContext {
}, },
Store: store, Store: store,
Signer: &mockSigner{}, Signer: &mockSigner{},
ChainIO: &mockChainIO{},
GenSweepScript: func() ([]byte, error) { GenSweepScript: func() ([]byte, error) {
script := []byte{outputScriptCount} script := []byte{outputScriptCount}
outputScriptCount++ outputScriptCount++

@ -10,12 +10,12 @@ import (
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet"
) )
var ( var (
defaultTestTimeout = 5 * time.Second defaultTestTimeout = 5 * time.Second
mockChainIOHeight = int32(100) mockChainHash, _ = chainhash.NewHashFromStr("00aabbccddeeff")
mockChainHeight = int32(100)
) )
type mockSigner struct { type mockSigner struct {
@ -155,12 +155,22 @@ func (m *MockNotifier) RegisterBlockEpochNtfn(
log.Tracef("Mock block ntfn registered") log.Tracef("Mock block ntfn registered")
m.mutex.Lock() m.mutex.Lock()
epochChan := make(chan *chainntnfs.BlockEpoch, 0) epochChan := make(chan *chainntnfs.BlockEpoch, 1)
bestHeight := int32(0)
if bestBlock != nil { // The real notifier returns a notification with the current block hash
bestHeight = bestBlock.Height // and height immediately if no best block hash or height is specified
// in the request. We want to emulate this behaviour as well for the
// mock.
switch {
case bestBlock == nil:
epochChan <- &chainntnfs.BlockEpoch{
Hash: mockChainHash,
Height: mockChainHeight,
}
m.epochChan[epochChan] = mockChainHeight
default:
m.epochChan[epochChan] = bestBlock.Height
} }
m.epochChan[epochChan] = bestHeight
m.mutex.Unlock() m.mutex.Unlock()
return &chainntnfs.BlockEpochEvent{ return &chainntnfs.BlockEpochEvent{
@ -235,25 +245,3 @@ func (m *MockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
}, },
}, nil }, nil
} }
type mockChainIO struct{}
var _ lnwallet.BlockChainIO = (*mockChainIO)(nil)
func (m *mockChainIO) GetBestBlock() (*chainhash.Hash, int32, error) {
return nil, mockChainIOHeight, nil
}
func (m *mockChainIO) GetUtxo(op *wire.OutPoint, pkScript []byte,
heightHint uint32, _ <-chan struct{}) (*wire.TxOut, error) {
return nil, nil
}
func (m *mockChainIO) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) {
return nil, nil
}
func (m *mockChainIO) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
return nil, nil
}