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 }