github.com/evdatsion/aphelion-dpos-bft@v0.32.1/lite/client/provider.go (about)

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