github.com/devwanda/aphelion-staking@v0.33.9/lite2/rpc/client.go (about)

     1  package rpc
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/devwanda/aphelion-staking/crypto/merkle"
    12  	tmbytes "github.com/devwanda/aphelion-staking/libs/bytes"
    13  	service "github.com/devwanda/aphelion-staking/libs/service"
    14  	lite "github.com/devwanda/aphelion-staking/lite2"
    15  	rpcclient "github.com/devwanda/aphelion-staking/rpc/client"
    16  	ctypes "github.com/devwanda/aphelion-staking/rpc/core/types"
    17  	rpctypes "github.com/devwanda/aphelion-staking/rpc/jsonrpc/types"
    18  	"github.com/devwanda/aphelion-staking/types"
    19  )
    20  
    21  var errNegOrZeroHeight = errors.New("negative or zero height")
    22  
    23  // Client is an RPC client, which uses lite#Client to verify data (if it can be
    24  // proved!).
    25  type Client struct {
    26  	service.BaseService
    27  
    28  	next rpcclient.Client
    29  	lc   *lite.Client
    30  	prt  *merkle.ProofRuntime
    31  }
    32  
    33  var _ rpcclient.Client = (*Client)(nil)
    34  
    35  // NewClient returns a new client.
    36  func NewClient(next rpcclient.Client, lc *lite.Client) *Client {
    37  	c := &Client{
    38  		next: next,
    39  		lc:   lc,
    40  		prt:  defaultProofRuntime(),
    41  	}
    42  	c.BaseService = *service.NewBaseService(nil, "Client", c)
    43  	return c
    44  }
    45  
    46  func (c *Client) OnStart() error {
    47  	if !c.next.IsRunning() {
    48  		return c.next.Start()
    49  	}
    50  	return nil
    51  }
    52  
    53  func (c *Client) OnStop() {
    54  	if c.next.IsRunning() {
    55  		c.next.Stop()
    56  	}
    57  }
    58  
    59  func (c *Client) Status() (*ctypes.ResultStatus, error) {
    60  	return c.next.Status()
    61  }
    62  
    63  func (c *Client) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
    64  	return c.next.ABCIInfo()
    65  }
    66  
    67  func (c *Client) ABCIQuery(path string, data tmbytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
    68  	return c.ABCIQueryWithOptions(path, data, rpcclient.DefaultABCIQueryOptions)
    69  }
    70  
    71  // GetWithProofOptions is useful if you want full access to the ABCIQueryOptions.
    72  // XXX Usage of path?  It's not used, and sometimes it's /, sometimes /key, sometimes /store.
    73  func (c *Client) ABCIQueryWithOptions(path string, data tmbytes.HexBytes,
    74  	opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
    75  
    76  	res, err := c.next.ABCIQueryWithOptions(path, data, opts)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	resp := res.Response
    81  
    82  	// Validate the response.
    83  	if resp.IsErr() {
    84  		return nil, fmt.Errorf("err response code: %v", resp.Code)
    85  	}
    86  	if len(resp.Key) == 0 || resp.Proof == nil {
    87  		return nil, errors.New("empty tree")
    88  	}
    89  	if resp.Height <= 0 {
    90  		return nil, errNegOrZeroHeight
    91  	}
    92  
    93  	// Update the light client if we're behind.
    94  	// NOTE: AppHash for height H is in header H+1.
    95  	h, err := c.updateLiteClientIfNeededTo(resp.Height + 1)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	// Validate the value proof against the trusted header.
   101  	if resp.Value != nil {
   102  		// Value exists
   103  		// XXX How do we encode the key into a string...
   104  		storeName, err := parseQueryStorePath(path)
   105  		if err != nil {
   106  			return nil, err
   107  		}
   108  		kp := merkle.KeyPath{}
   109  		kp = kp.AppendKey([]byte(storeName), merkle.KeyEncodingURL)
   110  		kp = kp.AppendKey(resp.Key, merkle.KeyEncodingURL)
   111  		err = c.prt.VerifyValue(resp.Proof, h.AppHash, kp.String(), resp.Value)
   112  		if err != nil {
   113  			return nil, fmt.Errorf("verify value proof: %w", err)
   114  		}
   115  		return &ctypes.ResultABCIQuery{Response: resp}, nil
   116  	}
   117  
   118  	// OR validate the ansence proof against the trusted header.
   119  	// XXX How do we encode the key into a string...
   120  	err = c.prt.VerifyAbsence(resp.Proof, h.AppHash, string(resp.Key))
   121  	if err != nil {
   122  		return nil, fmt.Errorf("verify absence proof: %w", err)
   123  	}
   124  	return &ctypes.ResultABCIQuery{Response: resp}, nil
   125  }
   126  
   127  func (c *Client) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
   128  	return c.next.BroadcastTxCommit(tx)
   129  }
   130  
   131  func (c *Client) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
   132  	return c.next.BroadcastTxAsync(tx)
   133  }
   134  
   135  func (c *Client) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
   136  	return c.next.BroadcastTxSync(tx)
   137  }
   138  
   139  func (c *Client) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) {
   140  	return c.next.UnconfirmedTxs(limit)
   141  }
   142  
   143  func (c *Client) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) {
   144  	return c.next.NumUnconfirmedTxs()
   145  }
   146  
   147  func (c *Client) NetInfo() (*ctypes.ResultNetInfo, error) {
   148  	return c.next.NetInfo()
   149  }
   150  
   151  func (c *Client) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
   152  	return c.next.DumpConsensusState()
   153  }
   154  
   155  func (c *Client) ConsensusState() (*ctypes.ResultConsensusState, error) {
   156  	return c.next.ConsensusState()
   157  }
   158  
   159  func (c *Client) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams, error) {
   160  	res, err := c.next.ConsensusParams(height)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	// Validate res.
   166  	if err := res.ConsensusParams.Validate(); err != nil {
   167  		return nil, err
   168  	}
   169  	if res.BlockHeight <= 0 {
   170  		return nil, errNegOrZeroHeight
   171  	}
   172  
   173  	// Update the light client if we're behind.
   174  	h, err := c.updateLiteClientIfNeededTo(res.BlockHeight)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  
   179  	// Verify hash.
   180  	if cH, tH := res.ConsensusParams.Hash(), h.ConsensusHash; !bytes.Equal(cH, tH) {
   181  		return nil, fmt.Errorf("params hash %X does not match trusted hash %X",
   182  			cH, tH)
   183  	}
   184  
   185  	return res, nil
   186  }
   187  
   188  func (c *Client) Health() (*ctypes.ResultHealth, error) {
   189  	return c.next.Health()
   190  }
   191  
   192  // BlockchainInfo calls rpcclient#BlockchainInfo and then verifies every header
   193  // returned.
   194  func (c *Client) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
   195  	res, err := c.next.BlockchainInfo(minHeight, maxHeight)
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  
   200  	// Validate res.
   201  	for i, meta := range res.BlockMetas {
   202  		if meta == nil {
   203  			return nil, fmt.Errorf("nil block meta %d", i)
   204  		}
   205  		if err := meta.ValidateBasic(); err != nil {
   206  			return nil, fmt.Errorf("invalid block meta %d: %w", i, err)
   207  		}
   208  	}
   209  
   210  	// Update the light client if we're behind.
   211  	if len(res.BlockMetas) > 0 {
   212  		lastHeight := res.BlockMetas[len(res.BlockMetas)-1].Header.Height
   213  		if _, err := c.updateLiteClientIfNeededTo(lastHeight); err != nil {
   214  			return nil, err
   215  		}
   216  	}
   217  
   218  	// Verify each of the BlockMetas.
   219  	for _, meta := range res.BlockMetas {
   220  		h, err := c.lc.TrustedHeader(meta.Header.Height)
   221  		if err != nil {
   222  			return nil, fmt.Errorf("trusted header %d: %w", meta.Header.Height, err)
   223  		}
   224  		if bmH, tH := meta.Header.Hash(), h.Hash(); !bytes.Equal(bmH, tH) {
   225  			return nil, fmt.Errorf("block meta header %X does not match with trusted header %X",
   226  				bmH, tH)
   227  		}
   228  	}
   229  
   230  	return res, nil
   231  }
   232  
   233  func (c *Client) Genesis() (*ctypes.ResultGenesis, error) {
   234  	return c.next.Genesis()
   235  }
   236  
   237  // Block calls rpcclient#Block and then verifies the result.
   238  func (c *Client) Block(height *int64) (*ctypes.ResultBlock, error) {
   239  	res, err := c.next.Block(height)
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  
   244  	// Validate res.
   245  	if err := res.BlockID.ValidateBasic(); err != nil {
   246  		return nil, err
   247  	}
   248  	if err := res.Block.ValidateBasic(); err != nil {
   249  		return nil, err
   250  	}
   251  	if bmH, bH := res.BlockID.Hash, res.Block.Hash(); !bytes.Equal(bmH, bH) {
   252  		return nil, fmt.Errorf("blockID %X does not match with block %X",
   253  			bmH, bH)
   254  	}
   255  
   256  	// Update the light client if we're behind.
   257  	h, err := c.updateLiteClientIfNeededTo(res.Block.Height)
   258  	if err != nil {
   259  		return nil, err
   260  	}
   261  
   262  	// Verify block.
   263  	if bH, tH := res.Block.Hash(), h.Hash(); !bytes.Equal(bH, tH) {
   264  		return nil, fmt.Errorf("block header %X does not match with trusted header %X",
   265  			bH, tH)
   266  	}
   267  
   268  	return res, nil
   269  }
   270  
   271  func (c *Client) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) {
   272  	res, err := c.next.BlockResults(height)
   273  	if err != nil {
   274  		return nil, err
   275  	}
   276  
   277  	// Validate res.
   278  	if res.Height <= 0 {
   279  		return nil, errNegOrZeroHeight
   280  	}
   281  
   282  	// Update the light client if we're behind.
   283  	h, err := c.updateLiteClientIfNeededTo(res.Height + 1)
   284  	if err != nil {
   285  		return nil, err
   286  	}
   287  
   288  	// Verify block results.
   289  	results := types.NewResults(res.TxsResults)
   290  	if rH, tH := results.Hash(), h.LastResultsHash; !bytes.Equal(rH, tH) {
   291  		return nil, fmt.Errorf("last results %X does not match with trusted last results %X",
   292  			rH, tH)
   293  	}
   294  
   295  	return res, nil
   296  }
   297  
   298  func (c *Client) Commit(height *int64) (*ctypes.ResultCommit, error) {
   299  	res, err := c.next.Commit(height)
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  
   304  	// Validate res.
   305  	if err := res.SignedHeader.ValidateBasic(c.lc.ChainID()); err != nil {
   306  		return nil, err
   307  	}
   308  	if res.Height <= 0 {
   309  		return nil, errNegOrZeroHeight
   310  	}
   311  
   312  	// Update the light client if we're behind.
   313  	h, err := c.updateLiteClientIfNeededTo(res.Height)
   314  	if err != nil {
   315  		return nil, err
   316  	}
   317  
   318  	// Verify commit.
   319  	if rH, tH := res.Hash(), h.Hash(); !bytes.Equal(rH, tH) {
   320  		return nil, fmt.Errorf("header %X does not match with trusted header %X",
   321  			rH, tH)
   322  	}
   323  
   324  	return res, nil
   325  }
   326  
   327  // Tx calls rpcclient#Tx method and then verifies the proof if such was
   328  // requested.
   329  func (c *Client) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
   330  	res, err := c.next.Tx(hash, prove)
   331  	if err != nil || !prove {
   332  		return res, err
   333  	}
   334  
   335  	// Validate res.
   336  	if res.Height <= 0 {
   337  		return nil, errNegOrZeroHeight
   338  	}
   339  
   340  	// Update the light client if we're behind.
   341  	h, err := c.updateLiteClientIfNeededTo(res.Height)
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  
   346  	// Validate the proof.
   347  	return res, res.Proof.Validate(h.DataHash)
   348  }
   349  
   350  func (c *Client) TxSearch(query string, prove bool, page, perPage int, orderBy string) (
   351  	*ctypes.ResultTxSearch, error) {
   352  	return c.next.TxSearch(query, prove, page, perPage, orderBy)
   353  }
   354  
   355  // Validators fetches and verifies validators.
   356  //
   357  // WARNING: only full validator sets are verified (when length of validators is
   358  // less than +perPage+. +perPage+ default is 30, max is 100).
   359  func (c *Client) Validators(height *int64, page, perPage int) (*ctypes.ResultValidators, error) {
   360  	res, err := c.next.Validators(height, page, perPage)
   361  	if err != nil {
   362  		return nil, err
   363  	}
   364  
   365  	// Validate res.
   366  	if res.BlockHeight <= 0 {
   367  		return nil, errNegOrZeroHeight
   368  	}
   369  
   370  	// Update the light client if we're behind.
   371  	h, err := c.updateLiteClientIfNeededTo(res.BlockHeight)
   372  	if err != nil {
   373  		return nil, err
   374  	}
   375  
   376  	// Verify validators.
   377  	if res.Count <= res.Total {
   378  		if rH, tH := types.NewValidatorSet(res.Validators).Hash(), h.ValidatorsHash; !bytes.Equal(rH, tH) {
   379  			return nil, fmt.Errorf("validators %X does not match with trusted validators %X",
   380  				rH, tH)
   381  		}
   382  	}
   383  
   384  	return res, nil
   385  }
   386  
   387  func (c *Client) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) {
   388  	return c.next.BroadcastEvidence(ev)
   389  }
   390  
   391  func (c *Client) Subscribe(ctx context.Context, subscriber, query string,
   392  	outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) {
   393  	return c.next.Subscribe(ctx, subscriber, query, outCapacity...)
   394  }
   395  
   396  func (c *Client) Unsubscribe(ctx context.Context, subscriber, query string) error {
   397  	return c.next.Unsubscribe(ctx, subscriber, query)
   398  }
   399  
   400  func (c *Client) UnsubscribeAll(ctx context.Context, subscriber string) error {
   401  	return c.next.UnsubscribeAll(ctx, subscriber)
   402  }
   403  
   404  func (c *Client) updateLiteClientIfNeededTo(height int64) (*types.SignedHeader, error) {
   405  	h, err := c.lc.VerifyHeaderAtHeight(height, time.Now())
   406  	if err != nil {
   407  		return nil, fmt.Errorf("failed to update light client to %d: %w", height, err)
   408  	}
   409  	return h, nil
   410  }
   411  
   412  func (c *Client) RegisterOpDecoder(typ string, dec merkle.OpDecoder) {
   413  	c.prt.RegisterOpDecoder(typ, dec)
   414  }
   415  
   416  // SubscribeWS subscribes for events using the given query and remote address as
   417  // a subscriber, but does not verify responses (UNSAFE)!
   418  // TODO: verify data
   419  func (c *Client) SubscribeWS(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) {
   420  	out, err := c.next.Subscribe(context.Background(), ctx.RemoteAddr(), query)
   421  	if err != nil {
   422  		return nil, err
   423  	}
   424  
   425  	go func() {
   426  		for {
   427  			select {
   428  			case resultEvent := <-out:
   429  				// We should have a switch here that performs a validation
   430  				// depending on the event's type.
   431  				ctx.WSConn.TryWriteRPCResponse(
   432  					rpctypes.NewRPCSuccessResponse(
   433  						ctx.WSConn.Codec(),
   434  						rpctypes.JSONRPCStringID(fmt.Sprintf("%v#event", ctx.JSONReq.ID)),
   435  						resultEvent,
   436  					))
   437  			case <-c.Quit():
   438  				return
   439  			}
   440  		}
   441  	}()
   442  
   443  	return &ctypes.ResultSubscribe{}, nil
   444  }
   445  
   446  // UnsubscribeWS calls original client's Unsubscribe using remote address as a
   447  // subscriber.
   448  func (c *Client) UnsubscribeWS(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe, error) {
   449  	err := c.next.Unsubscribe(context.Background(), ctx.RemoteAddr(), query)
   450  	if err != nil {
   451  		return nil, err
   452  	}
   453  	return &ctypes.ResultUnsubscribe{}, nil
   454  }
   455  
   456  // UnsubscribeAllWS calls original client's UnsubscribeAll using remote address
   457  // as a subscriber.
   458  func (c *Client) UnsubscribeAllWS(ctx *rpctypes.Context) (*ctypes.ResultUnsubscribe, error) {
   459  	err := c.next.UnsubscribeAll(context.Background(), ctx.RemoteAddr())
   460  	if err != nil {
   461  		return nil, err
   462  	}
   463  	return &ctypes.ResultUnsubscribe{}, nil
   464  }
   465  
   466  func parseQueryStorePath(path string) (storeName string, err error) {
   467  	if !strings.HasPrefix(path, "/") {
   468  		return "", errors.New("expected path to start with /")
   469  	}
   470  
   471  	paths := strings.SplitN(path[1:], "/", 3)
   472  	switch {
   473  	case len(paths) != 3:
   474  		return "", errors.New("expected format like /store/<storeName>/key")
   475  	case paths[0] != "store":
   476  		return "", errors.New("expected format like /store/<storeName>/key")
   477  	case paths[2] != "key":
   478  		return "", errors.New("expected format like /store/<storeName>/key")
   479  	}
   480  
   481  	return paths[1], nil
   482  }