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 }