github.com/decred/dcrlnd@v0.7.6/lnwallet/dcrwallet/spvsync.go (about)

     1  package dcrwallet
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"path/filepath"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/decred/dcrd/addrmgr/v2"
    11  	"github.com/decred/dcrd/chaincfg/v3"
    12  
    13  	"decred.org/dcrwallet/v4/p2p"
    14  	"decred.org/dcrwallet/v4/spv"
    15  )
    16  
    17  type SPVSyncerConfig struct {
    18  	Peers          []string
    19  	Net            *chaincfg.Params
    20  	AppDataDir     string
    21  	DialFunc       p2p.DialFunc
    22  	DisableRelayTx bool
    23  }
    24  
    25  // SPVSyncer implements the required methods for synchronizing a DcrWallet
    26  // instance using the SPV method.
    27  type SPVSyncer struct {
    28  	cfg *SPVSyncerConfig
    29  	wg  sync.WaitGroup
    30  
    31  	mtx sync.Mutex
    32  
    33  	// The following fields are protected by mtx.
    34  
    35  	cancel func()
    36  }
    37  
    38  // NewSPVSyncer initializes a new syncer backed by the dcrd network in SPV
    39  // mode.
    40  func NewSPVSyncer(cfg *SPVSyncerConfig) (*SPVSyncer, error) {
    41  	return &SPVSyncer{
    42  		cfg: cfg,
    43  	}, nil
    44  }
    45  
    46  // start the syncer backend and begin synchronizing the given wallet.
    47  func (s *SPVSyncer) start(w *DcrWallet) error {
    48  
    49  	lookup := net.LookupIP
    50  
    51  	disableDiscoverAccts, err := w.cfg.DB.AccountDiscoveryDisabled()
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	addr := &net.TCPAddr{IP: net.ParseIP("::1"), Port: 0}
    57  	amgrDir := filepath.Join(s.cfg.AppDataDir, s.cfg.Net.Name)
    58  	amgr := addrmgr.New(amgrDir, lookup)
    59  	lp := p2p.NewLocalPeer(s.cfg.Net, addr, amgr)
    60  	if s.cfg.DialFunc != nil {
    61  		lp.SetDialFunc(s.cfg.DialFunc)
    62  	}
    63  	lp.SetDisableRelayTx(s.cfg.DisableRelayTx)
    64  
    65  	syncer := spv.NewSyncer(w.wallet, lp)
    66  	if len(s.cfg.Peers) > 0 {
    67  		syncer.SetPersistentPeers(s.cfg.Peers)
    68  	}
    69  	w.wallet.SetNetworkBackend(syncer)
    70  
    71  	if disableDiscoverAccts {
    72  		syncer.DisableDiscoverAccounts()
    73  	}
    74  
    75  	syncer.SetNotifications(&spv.Notifications{
    76  		Synced: w.onSyncerSynced,
    77  	})
    78  
    79  	// This context will be canceled by `w` once its Stop() method is
    80  	// called.
    81  	ctx, cancel := context.WithCancel(context.Background())
    82  	s.mtx.Lock()
    83  	s.cancel = cancel
    84  	s.mtx.Unlock()
    85  
    86  	s.wg.Add(1)
    87  	go func() {
    88  		defer s.wg.Done()
    89  
    90  		for {
    91  			dcrwLog.Debugf("Starting SPV syncer")
    92  			if len(s.cfg.Peers) > 0 {
    93  				dcrwLog.Debugf("Forcing SPV to peers: %s", s.cfg.Peers)
    94  			}
    95  
    96  			err := syncer.Run(ctx)
    97  			select {
    98  			case <-ctx.Done():
    99  				// stop() requested.
   100  				return
   101  			default:
   102  				w.rpcSyncerFinished()
   103  				dcrwLog.Errorf("SPV synchronization ended: %v", err)
   104  			}
   105  
   106  			// Backoff for 5 seconds.
   107  			select {
   108  			case <-ctx.Done():
   109  				// Graceful shutdown.
   110  				dcrwLog.Debugf("RPCsyncer shutting down")
   111  				return
   112  			case <-time.After(5 * time.Second):
   113  			}
   114  		}
   115  	}()
   116  
   117  	return nil
   118  }
   119  
   120  func (s *SPVSyncer) stop() {
   121  	dcrwLog.Debugf("SPVSyncer requested shutdown")
   122  	s.mtx.Lock()
   123  	if s.cancel != nil {
   124  		s.cancel()
   125  		s.cancel = nil
   126  	}
   127  	s.mtx.Unlock()
   128  }
   129  
   130  func (s *SPVSyncer) waitForShutdown() {
   131  	s.wg.Wait()
   132  }