github.com/decred/dcrlnd@v0.7.6/lnwallet/remotedcrwallet/remotedcrwchainio.go (about)

     1  package remotedcrwallet
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"decred.org/dcrwallet/v4/rpc/walletrpc"
     8  	"github.com/decred/dcrd/chaincfg/chainhash"
     9  	"github.com/decred/dcrd/wire"
    10  
    11  	"github.com/decred/dcrlnd/chainscan"
    12  	"github.com/decred/dcrlnd/chainscan/csdrivers"
    13  	"github.com/decred/dcrlnd/lnwallet"
    14  	"github.com/decred/dcrlnd/lnwallet/dcrwallet"
    15  
    16  	"google.golang.org/grpc/codes"
    17  	"google.golang.org/grpc/status"
    18  )
    19  
    20  // Compile time check to ensure DcrWallet fulfills lnwallet.BlockChainIO.
    21  var _ lnwallet.BlockChainIO = (*DcrWallet)(nil)
    22  
    23  func runAndLogOnError(ctx context.Context, f func(context.Context) error, name string) {
    24  	go func() {
    25  		err := f(ctx)
    26  		select {
    27  		case <-ctx.Done():
    28  			// Any errs were due to done() so, ok
    29  			return
    30  		default:
    31  		}
    32  		if err != nil {
    33  			dcrwLog.Errorf("RemoteWallet error while running %s: %v", name, err)
    34  		}
    35  	}()
    36  }
    37  
    38  // GetBestBlock returns the current height and hash of the best known block
    39  // within the main chain.
    40  //
    41  // This method is a part of the lnwallet.BlockChainIO interface.
    42  func (b *DcrWallet) GetBestBlock() (*chainhash.Hash, int32, error) {
    43  	resp, err := b.wallet.BestBlock(b.ctx, &walletrpc.BestBlockRequest{})
    44  	if err != nil {
    45  		return nil, 0, err
    46  	}
    47  	bh, err := chainhash.NewHash(resp.Hash)
    48  	if err != nil {
    49  		return nil, 0, err
    50  	}
    51  	return bh, int32(resp.Height), nil
    52  }
    53  
    54  // GetUtxo returns the original output referenced by the passed outpoint that
    55  // create the target pkScript.
    56  //
    57  // This method is a part of the lnwallet.BlockChainIO interface.
    58  func (b *DcrWallet) GetUtxo(op *wire.OutPoint, pkScript []byte,
    59  	heightHint uint32, cancel <-chan struct{}) (*wire.TxOut, error) {
    60  
    61  	// Setup a context that is canceled when either the wallet or the passed
    62  	// cancelChan are canceled.
    63  	ctx, cancelCtx := context.WithCancel(b.ctx)
    64  	defer cancelCtx()
    65  	go func() {
    66  		select {
    67  		case <-cancel:
    68  			cancelCtx()
    69  		case <-ctx.Done():
    70  		}
    71  	}()
    72  
    73  	src := csdrivers.NewRemoteWalletCSDriver(b.wallet, b.network, b.cfg.BlockCache)
    74  	historical := chainscan.NewHistorical(src)
    75  	runAndLogOnError(ctx, src.Run, "GetUtxo.RemoteWalletCSDriver")
    76  	runAndLogOnError(ctx, historical.Run, "GetUtxo.Historical")
    77  
    78  	return dcrwallet.GetUtxoWithHistorical(ctx, historical, op, pkScript, heightHint)
    79  }
    80  
    81  // GetBlock returns a raw block from the server given its hash.
    82  //
    83  // This method is a part of the lnwallet.BlockChainIO interface.
    84  func (b *DcrWallet) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
    85  	return b.cfg.BlockCache.GetBlock(b.ctx, blockHash, b.getBlock)
    86  }
    87  
    88  func (b *DcrWallet) getBlock(ctx context.Context, blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
    89  	// TODO: unify with the driver on chainscan.
    90  	var (
    91  		resp *walletrpc.GetRawBlockResponse
    92  		err  error
    93  	)
    94  
    95  	req := &walletrpc.GetRawBlockRequest{
    96  		BlockHash: blockHash[:],
    97  	}
    98  
    99  	// If the response error code is 'Unavailable' it means the wallet
   100  	// isn't connected to any peers while in SPV mode. In that case, wait a
   101  	// bit and try again.
   102  	for stop := false; !stop; {
   103  		resp, err = b.network.GetRawBlock(ctx, req)
   104  		switch {
   105  		case status.Code(err) == codes.Unavailable:
   106  			dcrwLog.Warnf("Network unavailable from wallet; will try again in 5 seconds")
   107  			select {
   108  			case <-b.ctx.Done():
   109  				return nil, b.ctx.Err()
   110  			case <-time.After(5 * time.Second):
   111  			}
   112  		case err != nil:
   113  			return nil, err
   114  		default:
   115  			stop = true
   116  		}
   117  	}
   118  
   119  	bl := &wire.MsgBlock{}
   120  	err = bl.FromBytes(resp.Block)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	return bl, nil
   126  }
   127  
   128  // GetBlockHash returns the hash of the block in the best blockchain at the
   129  // given height.
   130  //
   131  // This method is a part of the lnwallet.BlockChainIO interface.
   132  func (b *DcrWallet) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) {
   133  	req := &walletrpc.BlockInfoRequest{
   134  		BlockHeight: int32(blockHeight),
   135  	}
   136  	resp, err := b.wallet.BlockInfo(b.ctx, req)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	return chainhash.NewHash(resp.BlockHash)
   141  }