Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,7 @@ func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context,
Birthday: walletInitParams.Birthday,
RecoveryWindow: walletInitParams.RecoveryWindow,
NetParams: d.cfg.ActiveNetParams.Params,
IsLocalNet: d.cfg.Bitcoin.IsLocalNetwork(),
CoinType: d.cfg.ActiveNetParams.CoinType,
Wallet: walletInitParams.Wallet,
LoaderOptions: []btcwallet.LoaderOption{dbs.WalletDB},
Expand Down
8 changes: 5 additions & 3 deletions lnwallet/btcwallet/btcwallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -1803,9 +1803,11 @@ func (b *BtcWallet) IsSynced() (bool, int64, error) {
}

// If the timestamp on the best header is more than 2 hours in the
// past, then we're not yet synced.
minus24Hours := time.Now().Add(-2 * time.Hour)
if blockHeader.Timestamp.Before(minus24Hours) {
// past, then we're not yet synced. This staleness check is skipped on
// local networks (regtest/simnet), where blocks are only mined on
// demand and the tip is expected to sit idle for long stretches.
minus2Hours := time.Now().Add(-2 * time.Hour)
if !b.cfg.IsLocalNet && blockHeader.Timestamp.Before(minus2Hours) {
return false, bestTimestamp, nil
}

Expand Down
113 changes: 113 additions & 0 deletions lnwallet/btcwallet/chainsync_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package btcwallet

import (
"testing"
"time"

"github.com/btcsuite/btcd/wire/v2"
"github.com/btcsuite/btcwallet/waddrmgr"
basewallet "github.com/btcsuite/btcwallet/wallet"
"github.com/lightningnetwork/lnd/lnmock"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)

// mockSyncedWallet is a minimal base wallet fake that satisfies the
// base.Interface used by BtcWallet. It embeds the interface so the compile-time
// contract is met, but only the two methods IsSynced actually calls are
// implemented; any other call would panic on the nil embedded interface, which
// is what we want as a guard against the test drifting.
type mockSyncedWallet struct {
basewallet.Interface

stamp waddrmgr.BlockStamp
chainSynced bool
}

func (m *mockSyncedWallet) SyncedTo() waddrmgr.BlockStamp {
return m.stamp
}

func (m *mockSyncedWallet) ChainSynced() bool {
return m.chainSynced
}

// TestIsSyncedStaleTip asserts that the tip-staleness check in IsSynced is
// skipped when the wallet is configured for a local network (regtest/simnet)
// but still enforced otherwise. On local networks blocks are only mined on
// demand, so an idle chain tip whose timestamp is well in the past must not
// flip synced_to_chain to false.
func TestIsSyncedStaleTip(t *testing.T) {
t.Parallel()

const bestHeight = int32(100)

testCases := []struct {
name string
isLocalNet bool
staleTip bool
wantSynced bool
}{{
name: "local net stale tip is still synced",
isLocalNet: true,
staleTip: true,
wantSynced: true,
}, {
name: "non-local net stale tip is not synced",
isLocalNet: false,
staleTip: true,
wantSynced: false,
}, {
name: "non-local net fresh tip is synced",
isLocalNet: false,
staleTip: false,
wantSynced: true,
}}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

// Pick a tip timestamp that is either comfortably
// outside the 2-hour staleness window or right at the
// current time.
tipTime := time.Now()
if tc.staleTip {
tipTime = tipTime.Add(-3 * time.Hour)
}

header := &wire.BlockHeader{Timestamp: tipTime}
bestHash := header.BlockHash()

mockChain := &lnmock.MockChain{}
mockChain.On("GetBestBlock").Return(
&bestHash, bestHeight, nil,
)
mockChain.On("GetBlockHeader", mock.Anything).Return(
header, nil,
)

// The wallet reports itself fully caught up to the same
// height as the backend's best block, so the only thing
// left to decide sync status is the tip-staleness check.

Check failure on line 92 in lnwallet/btcwallet/chainsync_test.go

View workflow job for this annotation

GitHub Actions / Lint code

the line is 81 characters long, which exceeds the maximum of 80 characters. (ll)
w := &BtcWallet{
wallet: &mockSyncedWallet{
stamp: waddrmgr.BlockStamp{
Height: bestHeight,
Hash: bestHash,
Timestamp: tipTime,
},
chainSynced: true,
},
cfg: &Config{
ChainSource: mockChain,
IsLocalNet: tc.isLocalNet,
},
}

synced, _, err := w.IsSynced()
require.NoError(t, err)
require.Equal(t, tc.wantSynced, synced)
})
}
}
6 changes: 6 additions & 0 deletions lnwallet/btcwallet/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ type Config struct {
// NetParams is the net parameters for the target chain.
NetParams *chaincfg.Params

// IsLocalNet indicates whether the wallet is running on a local
// network such as regtest or simnet, where blocks are only mined on
// demand and the chain tip is expected to sit idle for long stretches.
// When true, the tip-staleness check in IsSynced is skipped.
IsLocalNet bool

// CoinType specifies the BIP 44 coin type to be used for derivation.
CoinType uint32

Expand Down
Loading