Skip to content

Commit

Permalink
asset: create btcwallet+neutrino log dir and file without initialization
Browse files Browse the repository at this point in the history
This puts spv wallet logs in a new log path (logs -> spvlogs), which fixes
an issue where wallet recovery removes the existing spv log file. Now an
spv wallet log file is never deleted. Wallet recovery may only copy the
wallet log file.
  • Loading branch information
ukane-philemon authored Dec 2, 2022
1 parent 39030ca commit 4685064
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 60 deletions.
20 changes: 8 additions & 12 deletions client/asset/bch/spv.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import (

const (
DefaultM uint64 = 784931 // From bchutil. Used for gcs filters.
logDirName = "logs"
logDirName = "spvlogs"
neutrinoDBName = "neutrino.db"
defaultAcctNum = 0
)
Expand Down Expand Up @@ -115,19 +115,14 @@ func openSPVWallet(dir string, cfg *btc.WalletConfig, btcParams *chaincfg.Params

// createSPVWallet creates a new SPV wallet.
func createSPVWallet(privPass []byte, seed []byte, bday time.Time, dbDir string, log dex.Logger, extIdx, intIdx uint32, net *bchchaincfg.Params) error {
netDir := filepath.Join(dbDir, net.Name, "spv")
netDir := filepath.Join(dbDir, net.Name)
walletDir := filepath.Join(netDir, "spv")

if err := logNeutrino(netDir, log); err != nil {
return fmt.Errorf("error initializing bchwallet+neutrino logging: %w", err)
}

logDir := filepath.Join(netDir, logDirName)
err := os.MkdirAll(logDir, 0744)
if err != nil {
return fmt.Errorf("error creating wallet directories: %w", err)
}

loader := wallet.NewLoader(net, netDir, true, 250)
loader := wallet.NewLoader(net, walletDir, true, 250)

pubPass := []byte(wallet.InsecurePubPassphrase)

Expand All @@ -148,7 +143,7 @@ func createSPVWallet(privPass []byte, seed []byte, bday time.Time, dbDir string,
}

// The chain service DB
neutrinoDBPath := filepath.Join(netDir, neutrinoDBName)
neutrinoDBPath := filepath.Join(walletDir, neutrinoDBName)
db, err := walletdb.Create("bdb", neutrinoDBPath, true)
if err != nil {
return fmt.Errorf("unable to create neutrino db at %q: %w", neutrinoDBPath, err)
Expand All @@ -168,7 +163,8 @@ func createSPVWallet(privPass []byte, seed []byte, bday time.Time, dbDir string,
// Start initializes the *bchwallet.Wallet and its supporting players and starts
// syncing.
func (w *bchSPVWallet) Start() (btc.SPVService, error) {
if err := logNeutrino(w.dir, w.log); err != nil {
netDir := filepath.Dir(w.dir)
if err := logNeutrino(netDir, w.log); err != nil {
return nil, fmt.Errorf("error initializing bchwallet+neutrino logging: %v", err)
}
// recoverWindow arguments borrowed from bchwallet directly.
Expand Down Expand Up @@ -973,7 +969,7 @@ func logRotator(netDir string) (*rotator.Rotator, error) {
// only has to be initialized once, so an atomic flag is used internally to
// return early on subsequent invocations.
//
// In theory, the the rotating file logger must be Close'd at some point, but
// In theory, the rotating file logger must be Closed at some point, but
// there are concurrency issues with that since btcd and btcwallet have
// unsupervised goroutines still running after shutdown. So we leave the rotator
// running at the risk of losing some logs.
Expand Down
32 changes: 14 additions & 18 deletions client/asset/btc/spv.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"sync/atomic"
"time"
Expand Down Expand Up @@ -60,18 +59,14 @@ var _ BTCWallet = (*btcSPVWallet)(nil)

// createSPVWallet creates a new SPV wallet.
func createSPVWallet(privPass []byte, seed []byte, bday time.Time, dataDir string, log dex.Logger, extIdx, intIdx uint32, net *chaincfg.Params) error {
dir := filepath.Join(dataDir, net.Name, "spv")
if err := logNeutrino(dir); err != nil {
return fmt.Errorf("error initializing btcwallet+neutrino logging: %w", err)
}
netDir := filepath.Join(dataDir, net.Name)
walletDir := filepath.Join(netDir, spvDir)

logDir := filepath.Join(dir, logDirName)
err := os.MkdirAll(logDir, 0744)
if err != nil {
return fmt.Errorf("error creating wallet directories: %w", err)
if err := logNeutrino(netDir); err != nil {
return fmt.Errorf("error initializing btcwallet+neutrino logging: %w", err)
}

loader := wallet.NewLoader(net, dir, true, dbTimeout, 250)
loader := wallet.NewLoader(net, walletDir, true, dbTimeout, 250)

pubPass := []byte(wallet.InsecurePubPassphrase)

Expand All @@ -95,7 +90,7 @@ func createSPVWallet(privPass []byte, seed []byte, bday time.Time, dataDir strin
}

// The chain service DB
neutrinoDBPath := filepath.Join(dir, neutrinoDBName)
neutrinoDBPath := filepath.Join(walletDir, neutrinoDBName)
db, err := walletdb.Create("bdb", neutrinoDBPath, true, dbTimeout)
if err != nil {
bailOnWallet()
Expand Down Expand Up @@ -142,10 +137,11 @@ func (w *btcSPVWallet) updateDBBirthday(bday time.Time) error {
})
}

// startWallet initializes the *btcwallet.Wallet and its supporting players and
// Start initializes the *btcwallet.Wallet and its supporting players and
// starts syncing.
func (w *btcSPVWallet) Start() (SPVService, error) {
if err := logNeutrino(w.dir); err != nil {
netDir := filepath.Dir(w.dir)
if err := logNeutrino(netDir); err != nil {
return nil, fmt.Errorf("error initializing btcwallet+neutrino logging: %v", err)
}
// timeout and recoverWindow arguments borrowed from btcwallet directly.
Expand Down Expand Up @@ -242,7 +238,7 @@ func (w *btcSPVWallet) Start() (SPVService, error) {
return &btcChainService{w.cl}, nil
}

// stop stops the wallet and database threads.
// Stop stops the wallet and database threads.
func (w *btcSPVWallet) Stop() {
w.log.Info("Unloading wallet")
if err := w.loader.UnloadWallet(); err != nil {
Expand Down Expand Up @@ -351,7 +347,7 @@ func (w *btcSPVWallet) ForceRescan() {
}
}

// walletTransaction pulls the transaction from the database.
// WalletTransaction pulls the transaction from the database.
func (w *btcSPVWallet) WalletTransaction(txHash *chainhash.Hash) (*wtxmgr.TxDetails, error) {
details, err := wallet.UnstableAPI(w.Wallet).TxDetails(txHash)
if err != nil {
Expand Down Expand Up @@ -383,12 +379,12 @@ func (w *btcSPVWallet) SyncedTo() waddrmgr.BlockStamp {
// return err
// })
// if err != nil {
// return nil, err // sadly, waddrmgr.ErrBirthdayBlockNotSet is expected during most of chain sync
// return nil, err // sadly, waddrmgr.ErrBirthdayBlockNotSet is expected during most of the chain sync
// }
// return &birthdayBlock, nil
// }

// signTransaction signs the transaction inputs.
// SignTx signs the transaction inputs.
func (w *btcSPVWallet) SignTx(tx *wire.MsgTx) error {
var prevPkScripts [][]byte
var inputValues []btcutil.Amount
Expand Down Expand Up @@ -500,7 +496,7 @@ func (s *secretSource) GetScript(addr btcutil.Address) ([]byte, error) {
// only has to be initialized once, so an atomic flag is used internally to
// return early on subsequent invocations.
//
// In theory, the the rotating file logger must be Close'd at some point, but
// In theory, the rotating file logger must be Closed at some point, but
// there are concurrency issues with that since btcd and btcwallet have
// unsupervised goroutines still running after shutdown. So we leave the rotator
// running at the risk of losing some logs.
Expand Down
80 changes: 76 additions & 4 deletions client/asset/btc/spv_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"math"
"os"
"path/filepath"
Expand Down Expand Up @@ -71,7 +72,8 @@ const (

maxFutureBlockTime = 2 * time.Hour // see MaxTimeOffsetSeconds in btcd/blockchain/validate.go
neutrinoDBName = "neutrino.db"
logDirName = "logs"
spvDir = "spv"
logDirName = "spvlogs"
logFileName = "neutrino.log"
defaultAcctNum = 0
defaultAcctName = "default"
Expand Down Expand Up @@ -1082,7 +1084,7 @@ func (w *spvWallet) getBestBlockHeader() (*blockHeader, error) {
}

func (w *spvWallet) logFilePath() string {
return filepath.Join(w.dir, logDirName, logFileName)
return filepath.Join(filepath.Dir(w.dir), logDirName, logFileName)
}

// connect will start the wallet and begin syncing.
Expand Down Expand Up @@ -1153,8 +1155,78 @@ func (w *spvWallet) moveWalletData(backupDir string) error {
if err != nil {
return err
}
backupFolder := filepath.Join(backupDir, timeString)
return os.Rename(w.dir, backupFolder)

backupFolder := filepath.Join(backupDir, w.chainParams.Name, timeString)
// Copy wallet logs first. Even if there is an error, wallet files are
// still intact.
backupLogDir := filepath.Join(backupFolder, logDirName)
walletLogDir := filepath.Dir(w.logFilePath())
if err := copyDir(walletLogDir, backupLogDir); err != nil {
return err
}

walletBackupDir := filepath.Join(backupFolder, spvDir)
if err := os.Rename(w.dir, walletBackupDir); err != nil {
return err
}
return nil
}

// copyFile copies a file from src to dst.
func copyFile(src, dst string) error {
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()

in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()

_, err = io.Copy(out, in)
return err
}

// copyDir recursively copies the directories and files in source directory to
// destination directory without preserving the original file permissions. The
// destination folder must not exist.
func copyDir(src, dst string) error {
entries, err := os.ReadDir(src)
if err != nil {
return err
}

fi, err := os.Stat(dst)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return err
}
err = os.MkdirAll(dst, 0744)
if err != nil {
return err
}
} else if !fi.IsDir() {
return fmt.Errorf("%q is not a directory", dst)
}

for _, fd := range entries {
fName := fd.Name()
srcFile := filepath.Join(src, fName)
dstFile := filepath.Join(dst, fName)
if fd.IsDir() {
err = copyDir(srcFile, dstFile)
} else if fd.Type().IsRegular() {
err = copyFile(srcFile, dstFile)
}
if err != nil {
return err
}
}

return nil
}

// numDerivedAddresses returns the number of internal and external addresses
Expand Down
30 changes: 16 additions & 14 deletions client/asset/dcr/spv.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const (
defaultAcctName = "default"
walletDbName = "wallet.db"
dbDriver = "bdb"
logDirName = "logs"
logDirName = "spvlogs"
logFileName = "neutrino.log"
)

Expand Down Expand Up @@ -138,22 +138,23 @@ var _ Wallet = (*spvWallet)(nil)
var _ tipNotifier = (*spvWallet)(nil)

func createSPVWallet(pw, seed []byte, dataDir string, extIdx, intIdx uint32, chainParams *chaincfg.Params) error {
dir := filepath.Join(dataDir, chainParams.Name, "spv")
netDir := filepath.Join(dataDir, chainParams.Name)
walletDir := filepath.Join(netDir, "spv")

if err := initLogging(dir); err != nil {
if err := initLogging(netDir); err != nil {
return fmt.Errorf("error initializing dcrwallet logging: %w", err)
}

if exists, err := walletExists(dir); err != nil {
if exists, err := walletExists(walletDir); err != nil {
return err
} else if exists {
return fmt.Errorf("wallet at %q already exists", dir)
return fmt.Errorf("wallet at %q already exists", walletDir)
}

ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

dbPath := filepath.Join(dir, walletDbName)
dbPath := filepath.Join(walletDir, walletDbName)
exists, err := fileExists(dbPath)
if err != nil {
return fmt.Errorf("error checking file existence for %q: %w", dbPath, err)
Expand All @@ -163,7 +164,7 @@ func createSPVWallet(pw, seed []byte, dataDir string, extIdx, intIdx uint32, cha
}

// Ensure the data directory for the network exists.
if err := checkCreateDir(dir); err != nil {
if err := checkCreateDir(walletDir); err != nil {
return fmt.Errorf("checkCreateDir error: %w", err)
}

Expand All @@ -172,7 +173,7 @@ func createSPVWallet(pw, seed []byte, dataDir string, extIdx, intIdx uint32, cha
// attempts to remove any wallet remnants.
defer func() {
if err != nil {
_ = os.Remove(dir)
_ = os.Remove(walletDir)
}
}()

Expand Down Expand Up @@ -232,7 +233,8 @@ func (w *spvWallet) Reconfigure(ctx context.Context, cfg *asset.WalletConfig, ne
}

func (w *spvWallet) startWallet(ctx context.Context) error {
if err := initLogging(w.dir); err != nil {
netDir := filepath.Dir(w.dir)
if err := initLogging(netDir); err != nil {
return fmt.Errorf("error initializing dcrwallet logging: %w", err)
}

Expand Down Expand Up @@ -952,11 +954,11 @@ func logRotator(netDir string) (*rotator.Rotator, error) {
// to be initialized once, so an atomic flag is used internally to return early
// on subsequent invocations.
//
// TODO: See if the below precaution is even necessary for dcrwallet.
// // In theory, the the rotating file logger must be Close'd at some point, but
// // there are concurrency issues with that since btcd and btcwallet have
// // unsupervised goroutines still running after shutdown. So we leave the rotator
// // running at the risk of losing some logs.
// TODO: See if the below precaution is even necessary for dcrwallet. In theory,
// the the rotating file logger must be Close'd at some point, but there are
// concurrency issues with that since btcd and btcwallet have unsupervised
// goroutines still running after shutdown. So we leave the rotator running at
// the risk of losing some logs.
func initLogging(netDir string) error {
if !atomic.CompareAndSwapUint32(&loggingInited, 0, 1) {
return nil
Expand Down
Loading

0 comments on commit 4685064

Please sign in to comment.