github.com/franono/tendermint@v0.32.2-0.20200527150959-749313264ce9/lite/client/provider.go (about)

     1  /*
     2  Package client defines a provider that uses a rpchttp
     3  to get information, which is used to get new headers
     4  and validators directly from a Tendermint client.
     5  */
     6  package client
     7  
     8  import (
     9  	"fmt"
    10  
    11  	log "github.com/franono/tendermint/libs/log"
    12  	"github.com/franono/tendermint/lite"
    13  	lerr "github.com/franono/tendermint/lite/errors"
    14  	rpcclient "github.com/franono/tendermint/rpc/client"
    15  	rpchttp "github.com/franono/tendermint/rpc/client/http"
    16  	ctypes "github.com/franono/tendermint/rpc/core/types"
    17  	"github.com/franono/tendermint/types"
    18  )
    19  
    20  // SignStatusClient combines a SignClient and StatusClient.
    21  type SignStatusClient interface {
    22  	rpcclient.SignClient
    23  	rpcclient.StatusClient
    24  }
    25  
    26  type provider struct {
    27  	logger  log.Logger
    28  	chainID string
    29  	client  SignStatusClient
    30  }
    31  
    32  // NewProvider implements Provider (but not PersistentProvider).
    33  func NewProvider(chainID string, client SignStatusClient) lite.Provider {
    34  	return &provider{
    35  		logger:  log.NewNopLogger(),
    36  		chainID: chainID,
    37  		client:  client,
    38  	}
    39  }
    40  
    41  // NewHTTPProvider can connect to a tendermint json-rpc endpoint
    42  // at the given url, and uses that as a read-only provider.
    43  func NewHTTPProvider(chainID, remote string) (lite.Provider, error) {
    44  	httpClient, err := rpchttp.New(remote, "/websocket")
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	return NewProvider(chainID, httpClient), nil
    49  }
    50  
    51  // Implements Provider.
    52  func (p *provider) SetLogger(logger log.Logger) {
    53  	logger = logger.With("module", "lite/client")
    54  	p.logger = logger
    55  }
    56  
    57  // StatusClient returns the internal client as a StatusClient
    58  func (p *provider) StatusClient() rpcclient.StatusClient {
    59  	return p.client
    60  }
    61  
    62  // LatestFullCommit implements Provider.
    63  func (p *provider) LatestFullCommit(chainID string, minHeight, maxHeight int64) (fc lite.FullCommit, err error) {
    64  	if chainID != p.chainID {
    65  		err = fmt.Errorf("expected chainID %s, got %s", p.chainID, chainID)
    66  		return
    67  	}
    68  	if maxHeight != 0 && maxHeight < minHeight {
    69  		err = fmt.Errorf("need maxHeight == 0 or minHeight <= maxHeight, got min %v and max %v",
    70  			minHeight, maxHeight)
    71  		return
    72  	}
    73  	commit, err := p.fetchLatestCommit(minHeight, maxHeight)
    74  	if err != nil {
    75  		return
    76  	}
    77  	fc, err = p.fillFullCommit(commit.SignedHeader)
    78  	return
    79  }
    80  
    81  // fetchLatestCommit fetches the latest commit from the client.
    82  func (p *provider) fetchLatestCommit(minHeight int64, maxHeight int64) (*ctypes.ResultCommit, error) {
    83  	status, err := p.client.Status()
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	if status.SyncInfo.LatestBlockHeight < minHeight {
    88  		err = fmt.Errorf("provider is at %v but require minHeight=%v",
    89  			status.SyncInfo.LatestBlockHeight, minHeight)
    90  		return nil, err
    91  	}
    92  	if maxHeight == 0 {
    93  		maxHeight = status.SyncInfo.LatestBlockHeight
    94  	} else if status.SyncInfo.LatestBlockHeight < maxHeight {
    95  		maxHeight = status.SyncInfo.LatestBlockHeight
    96  	}
    97  	return p.client.Commit(&maxHeight)
    98  }
    99  
   100  // Implements Provider.
   101  func (p *provider) ValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
   102  	return p.getValidatorSet(chainID, height)
   103  }
   104  
   105  func (p *provider) getValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
   106  	if chainID != p.chainID {
   107  		err = fmt.Errorf("expected chainID %s, got %s", p.chainID, chainID)
   108  		return
   109  	}
   110  	if height < 1 {
   111  		err = fmt.Errorf("expected height >= 1, got height %v", height)
   112  		return
   113  	}
   114  	res, err := p.client.Validators(&height, 0, 0)
   115  	if err != nil {
   116  		// TODO pass through other types of errors.
   117  		return nil, lerr.ErrUnknownValidators(chainID, height)
   118  	}
   119  	valset = types.NewValidatorSet(res.Validators)
   120  	return
   121  }
   122  
   123  // This does no validation.
   124  func (p *provider) fillFullCommit(signedHeader types.SignedHeader) (fc lite.FullCommit, err error) {
   125  
   126  	// Get the validators.
   127  	valset, err := p.getValidatorSet(signedHeader.ChainID, signedHeader.Height)
   128  	if err != nil {
   129  		return lite.FullCommit{}, err
   130  	}
   131  
   132  	// Get the next validators.
   133  	nextValset, err := p.getValidatorSet(signedHeader.ChainID, signedHeader.Height+1)
   134  	if err != nil {
   135  		return lite.FullCommit{}, err
   136  	}
   137  
   138  	return lite.NewFullCommit(signedHeader, valset, nextValset), nil
   139  }