github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/tendermint/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/fibonacci-chain/fbc/libs/tendermint/libs/log"
    12  	"github.com/fibonacci-chain/fbc/libs/tendermint/lite"
    13  	lerr "github.com/fibonacci-chain/fbc/libs/tendermint/lite/errors"
    14  	rpcclient "github.com/fibonacci-chain/fbc/libs/tendermint/rpc/client"
    15  	rpchttp "github.com/fibonacci-chain/fbc/libs/tendermint/rpc/client/http"
    16  	ctypes "github.com/fibonacci-chain/fbc/libs/tendermint/rpc/core/types"
    17  	"github.com/fibonacci-chain/fbc/libs/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 maxHeight != 0 && maxHeight < minHeight {
    65  		err = fmt.Errorf("need maxHeight == 0 or minHeight <= maxHeight, got min %v and max %v",
    66  			minHeight, maxHeight)
    67  		return
    68  	}
    69  	commit, err := p.fetchLatestCommit(minHeight, maxHeight)
    70  	if err != nil {
    71  		return
    72  	}
    73  	if !verifyEqualChainId(commit.ChainID, p.chainID, commit.Height) {
    74  		err = fmt.Errorf("expected chainID %s, got %s", p.chainID, commit.ChainID)
    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 !verifyEqualChainId(chainID, p.chainID, height) {
   107  		err = fmt.Errorf("expected chainID %s, got %s", p.chainID, chainID)
   108  		return
   109  	}
   110  	if height < types.GetStartBlockHeight()+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  	// Get the validators.
   126  	valset, err := p.getValidatorSet(signedHeader.ChainID, signedHeader.Height)
   127  	if err != nil {
   128  		return lite.FullCommit{}, err
   129  	}
   130  
   131  	// Get the next validators.
   132  	nextValset, err := p.getValidatorSet(signedHeader.ChainID, signedHeader.Height+1)
   133  	if err != nil {
   134  		return lite.FullCommit{}, err
   135  	}
   136  
   137  	return lite.NewFullCommit(signedHeader, valset, nextValset), nil
   138  }
   139  
   140  func verifyEqualChainId(chain1 string, flagChainId string, h int64) bool {
   141  	if flagChainId == types.TestNet {
   142  		if h < types.TestNetChangeChainId && chain1 == types.TestNetChainName1 {
   143  			return true
   144  		}
   145  	}
   146  	return chain1 == flagChainId
   147  }