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  }