github.com/decred/dcrlnd@v0.7.6/lnwallet/dcrwallet/rpcsync.go (about) 1 package dcrwallet 2 3 import ( 4 "context" 5 "sync" 6 "time" 7 8 "github.com/decred/dcrd/chaincfg/v3" 9 "github.com/decred/dcrd/rpcclient/v8" 10 11 "decred.org/dcrwallet/v4/chain" 12 "decred.org/dcrwallet/v4/errors" 13 ) 14 15 // RPCSyncer implements the required methods for synchronizing a DcrWallet 16 // instance using a full node dcrd backend. 17 type RPCSyncer struct { 18 rpcConfig rpcclient.ConnConfig 19 net *chaincfg.Params 20 wg sync.WaitGroup 21 22 mtx sync.Mutex 23 24 // The following fields are protected by mtx. 25 26 cancel func() 27 } 28 29 // NewRPCSyncer initializes a new syncer backed by a full dcrd node. It 30 // requires the config for reaching the dcrd instance and the corresponding 31 // network this instance should be in. 32 func NewRPCSyncer(rpcConfig rpcclient.ConnConfig, net *chaincfg.Params) (*RPCSyncer, error) { 33 return &RPCSyncer{ 34 rpcConfig: rpcConfig, 35 net: net, 36 }, nil 37 } 38 39 // start the syncer backend and begin synchronizing the given wallet. 40 func (s *RPCSyncer) start(w *DcrWallet) error { 41 42 chainRpcOpts := chain.RPCOptions{ 43 Address: s.rpcConfig.Host, 44 User: s.rpcConfig.User, 45 Pass: s.rpcConfig.Pass, 46 CA: s.rpcConfig.Certificates, 47 } 48 49 disableDiscoverAccts, err := w.cfg.DB.AccountDiscoveryDisabled() 50 if err != nil { 51 return err 52 } 53 54 s.wg.Add(1) 55 go func() { 56 defer s.wg.Done() 57 for { 58 // This context will be canceled by `w` once its Stop() method is 59 // called. 60 ctx, cancel := context.WithCancel(context.Background()) 61 s.mtx.Lock() 62 s.cancel = cancel 63 s.mtx.Unlock() 64 65 syncer := chain.NewSyncer(w.wallet, &chainRpcOpts) 66 syncer.SetCallbacks(&chain.Callbacks{ 67 Synced: w.onSyncerSynced, 68 }) 69 70 if disableDiscoverAccts { 71 syncer.DisableDiscoverAccounts() 72 } 73 74 dcrwLog.Debugf("Starting rpc syncer") 75 err := syncer.Run(ctx) 76 w.rpcSyncerFinished() 77 78 // TODO: convert to errors.Is 79 if werr, is := err.(*errors.Error); is && werr.Err == context.Canceled { 80 // This was a graceful shutdown, so ignore the error. 81 dcrwLog.Debugf("RPCsyncer shutting down") 82 return 83 } 84 dcrwLog.Errorf("RPCSyncer error: %v", err) 85 86 // Backoff for 5 seconds. 87 select { 88 case <-ctx.Done(): 89 // Graceful shutdown. 90 dcrwLog.Debugf("RPCsyncer shutting down") 91 return 92 case <-time.After(5 * time.Second): 93 } 94 95 // Clear and call s.cancel() so we don't leak it. 96 s.mtx.Lock() 97 s.cancel = nil 98 s.mtx.Unlock() 99 cancel() 100 } 101 }() 102 103 return nil 104 } 105 106 func (s *RPCSyncer) stop() { 107 dcrwLog.Debugf("RPCSyncer requested shutdown") 108 s.mtx.Lock() 109 if s.cancel != nil { 110 s.cancel() 111 s.cancel = nil 112 } 113 s.mtx.Unlock() 114 } 115 116 func (s *RPCSyncer) waitForShutdown() { 117 s.wg.Wait() 118 }