Skip to content

Commit e73d622

Browse files
committed
waved: add wallet.dbbackend for the lwwallet backend
Expose lwwallet's wallet database backend selection as a daemon config option and CLI flag, so the network directory can be made to hold SQLite databases only. The default is unchanged. The value is rejected at config-validation time rather than passed through blindly: an unknown backend would otherwise only surface when the operator first tries to create or unlock the wallet, which for an auto-unlocking daemon means a failed startup with the cause several layers down. The option is deliberately not settable per wallet lifetime: lwwallet refuses to resolve a backend whose counterpart's database file is already present, so a change on an initialized network directory fails with the file it found rather than creating an empty second wallet.
1 parent 8843067 commit e73d622

7 files changed

Lines changed: 97 additions & 2 deletions

File tree

cmd/waved/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,10 @@ func registerWalletFlags(f *pflag.FlagSet, cfg *waved.Config) {
338338
"wallet.esploraurl", cfg.Wallet.EsploraURL,
339339
"esplora REST API URL (required for lwwallet)",
340340
)
341+
f.String(
342+
"wallet.dbbackend", cfg.Wallet.DBBackend,
343+
"wallet database backend for lwwallet (bolt, sqlite)",
344+
)
341345
f.String(
342346
"wallet.feeurl", cfg.Wallet.FeeURL,
343347
"fee-estimate JSON endpoint URL (required for btcwallet)",

docs/daemon_cli_guide.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ waved \
130130
| `--wallet.btcwallet_filterheaderssource` | | Filter header import source for btcwallet fast sync |
131131
| `--wallet.pollinterval` | `30s` | Esplora poll interval (lwwallet only) |
132132
| `--wallet.recoverywindow` | `100` | Address look-ahead window (lwwallet only) |
133+
| `--wallet.dbbackend` | `bolt` | Wallet database backend (lwwallet only): `bolt` or `sqlite`; fixed once the wallet is created |
133134
| `--wallet.password_file` | | Auto-unlock password file path (lwwallet/btcwallet) |
134135
| `--lnd.host` | `localhost:10009` | lnd gRPC address |
135136
| `--lnd.tlspath` | | Path to lnd TLS certificate |

sample-waved.conf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,13 @@
192192
# Address recovery look-ahead window for lwwallet.
193193
# wallet.recoverywindow=100
194194

195+
# Database backend for the lwwallet wallet database: bolt or sqlite. Both keep
196+
# the database in the network data directory. Pick sqlite to keep the directory
197+
# free of a BoltDB file, for instance so a backup can copy it with a single
198+
# storage engine's guarantees. The backend cannot be changed once the wallet
199+
# has been created.
200+
# wallet.dbbackend=bolt
201+
195202
# Path to a file containing the wallet password for auto-unlock at startup.
196203
# wallet.password_file=
197204

waved/config.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,13 @@ type WalletConfig struct {
11231123
// automatically without requiring an UnlockWallet RPC call.
11241124
PasswordFile string `mapstructure:"password_file"`
11251125

1126+
// DBBackend selects the database engine the lwwallet backend
1127+
// keeps btcwallet's wallet database in: "bolt" (the default) uses
1128+
// the classic BoltDB file, "sqlite" uses a SQLite database so the
1129+
// network directory holds no BoltDB file at all. Both live in the
1130+
// network directory. Only used when Type is "lwwallet".
1131+
DBBackend string `mapstructure:"dbbackend"`
1132+
11261133
// BtcwalletPeers is a list of host:port addresses for neutrino
11271134
// to connect to exclusively (no DNS seeding). Only used when
11281135
// Type is "btcwallet".
@@ -1198,6 +1205,7 @@ func DefaultConfig() *Config {
11981205
Type: DefaultWalletType,
11991206
PollInterval: DefaultEsploraPollInterval,
12001207
RecoveryWindow: DefaultRecoveryWindow,
1208+
DBBackend: lwwallet.DBBackendBolt,
12011209
},
12021210
Swap: &SwapConfig{
12031211
ServerTransport: RPCTransportGRPC,
@@ -1401,6 +1409,20 @@ func (c *Config) validateWalletConfig() error {
14011409
c.Wallet.EsploraURL = esploraURL
14021410
}
14031411

1412+
// Reject an unknown database backend here rather than at
1413+
// wallet-unlock time: a typo would otherwise only surface
1414+
// once the operator tries to use the wallet.
1415+
switch c.Wallet.DBBackend {
1416+
case "", lwwallet.DBBackendBolt, lwwallet.DBBackendSQLite:
1417+
// An empty value keeps the lwwallet default.
1418+
1419+
default:
1420+
return fmt.Errorf("unknown wallet.dbbackend %q, valid "+
1421+
"values: %s, %s", c.Wallet.DBBackend,
1422+
lwwallet.DBBackendBolt,
1423+
lwwallet.DBBackendSQLite)
1424+
}
1425+
14041426
case WalletTypeBtcwallet:
14051427
// Neutrino has no mempool visibility, so fee estimation
14061428
// always requires an external API. An empty value falls
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package waved
2+
3+
import (
4+
"testing"
5+
6+
"github.com/lightninglabs/wavelength/lwwallet"
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
// TestConfigValidateWalletDBBackend checks that the lwwallet database
11+
// backend selector only accepts the backends lwwallet implements, so a
12+
// typo fails at startup instead of at wallet-unlock time.
13+
func TestConfigValidateWalletDBBackend(t *testing.T) {
14+
t.Parallel()
15+
16+
tests := []struct {
17+
name string
18+
backend string
19+
wantErr string
20+
}{
21+
{
22+
name: "unset keeps the default",
23+
},
24+
{
25+
name: "bolt",
26+
backend: lwwallet.DBBackendBolt,
27+
},
28+
{
29+
name: "sqlite",
30+
backend: lwwallet.DBBackendSQLite,
31+
},
32+
{
33+
name: "unknown backend",
34+
backend: "postgres",
35+
wantErr: "unknown wallet.dbbackend",
36+
},
37+
}
38+
39+
for _, tc := range tests {
40+
t.Run(tc.name, func(t *testing.T) {
41+
t.Parallel()
42+
43+
cfg := DefaultConfig()
44+
cfg.Network = "regtest"
45+
cfg.Wallet.Type = WalletTypeLwwallet
46+
cfg.Wallet.EsploraURL = "http://127.0.0.1:3000"
47+
cfg.Wallet.DBBackend = tc.backend
48+
49+
err := cfg.Validate()
50+
if tc.wantErr != "" {
51+
require.ErrorContains(t, err, tc.wantErr)
52+
53+
return
54+
}
55+
56+
require.NoError(t, err)
57+
})
58+
}
59+
}

waved/rpc_wallet.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -696,14 +696,15 @@ func isWrongPassphraseErr(err error) bool {
696696

697697
// selfManagedWalletExists probes whether a wallet database has been
698698
// created for the configured self-managed wallet backend. Only the
699-
// chain params and data directory are consulted; the probe does not
700-
// open the database on native builds.
699+
// chain params, data directory and database backend are consulted; the
700+
// probe does not open a database that does not exist yet.
701701
func (s *Server) selfManagedWalletExists() (bool, error) {
702702
switch s.cfg.Wallet.Type {
703703
case WalletTypeLwwallet:
704704
return lwwallet.WalletExists(lwwallet.Config{
705705
ChainParams: s.chainParams,
706706
DBDir: s.cfg.NetworkDir(),
707+
DBBackend: s.cfg.Wallet.DBBackend,
707708
})
708709

709710
case WalletTypeBtcwallet:

waved/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1993,6 +1993,7 @@ func (s *Server) startLwwallet(ctx context.Context, seed []byte,
19931993
PollInterval: pollInterval,
19941994
RecoveryWindow: recoveryWindow,
19951995
DBDir: networkDir,
1996+
DBBackend: s.cfg.Wallet.DBBackend,
19961997
Log: fn.Some(s.subLogger(lwwallet.Subsystem)),
19971998
})
19981999
if err != nil {

0 commit comments

Comments
 (0)