github.com/okex/exchain@v1.8.0/libs/tendermint/lite/proxy/wrapper.go (about)

     1  package proxy
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/okex/exchain/libs/tendermint/crypto/merkle"
     7  	"github.com/okex/exchain/libs/tendermint/libs/bytes"
     8  	"github.com/okex/exchain/libs/tendermint/lite"
     9  	rpcclient "github.com/okex/exchain/libs/tendermint/rpc/client"
    10  	ctypes "github.com/okex/exchain/libs/tendermint/rpc/core/types"
    11  	rpctypes "github.com/okex/exchain/libs/tendermint/rpc/jsonrpc/types"
    12  )
    13  
    14  var _ rpcclient.Client = Wrapper{}
    15  
    16  // Wrapper wraps a rpcclient with a Verifier and double-checks any input that is
    17  // provable before passing it along. Allows you to make any rpcclient fully secure.
    18  type Wrapper struct {
    19  	rpcclient.Client
    20  	cert *lite.DynamicVerifier
    21  	prt  *merkle.ProofRuntime
    22  }
    23  
    24  // SecureClient uses a given Verifier to wrap an connection to an untrusted
    25  // host and return a cryptographically secure rpc client.
    26  //
    27  // If it is wrapping an HTTP rpcclient, it will also wrap the websocket interface
    28  func SecureClient(c rpcclient.Client, cert *lite.DynamicVerifier) Wrapper {
    29  	prt := defaultProofRuntime()
    30  	wrap := Wrapper{c, cert, prt}
    31  	// TODO: no longer possible as no more such interface exposed....
    32  	// if we wrap http client, then we can swap out the event switch to filter
    33  	// if hc, ok := c.(*rpcclient.HTTP); ok {
    34  	// 	evt := hc.WSEvents.EventSwitch
    35  	// 	hc.WSEvents.EventSwitch = WrappedSwitch{evt, wrap}
    36  	// }
    37  	return wrap
    38  }
    39  
    40  // ABCIQueryWithOptions exposes all options for the ABCI query and verifies the returned proof
    41  func (w Wrapper) ABCIQueryWithOptions(path string, data bytes.HexBytes,
    42  	opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
    43  
    44  	res, err := GetWithProofOptions(w.prt, path, data, opts, w.Client, w.cert)
    45  	return res, err
    46  }
    47  
    48  // ABCIQuery uses default options for the ABCI query and verifies the returned proof
    49  func (w Wrapper) ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
    50  	return w.ABCIQueryWithOptions(path, data, rpcclient.DefaultABCIQueryOptions)
    51  }
    52  
    53  // Tx queries for a given tx and verifies the proof if it was requested
    54  func (w Wrapper) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
    55  	res, err := w.Client.Tx(hash, prove)
    56  	if !prove || err != nil {
    57  		return res, err
    58  	}
    59  	h := res.Height
    60  	sh, err := GetCertifiedCommit(h, w.Client, w.cert)
    61  	if err != nil {
    62  		return res, err
    63  	}
    64  	err = res.Proof.Validate(sh.DataHash, res.Height)
    65  	return res, err
    66  }
    67  
    68  // BlockchainInfo requests a list of headers and verifies them all...
    69  // Rather expensive.
    70  //
    71  // TODO: optimize this if used for anything needing performance
    72  func (w Wrapper) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
    73  	r, err := w.Client.BlockchainInfo(minHeight, maxHeight)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	// go and verify every blockmeta in the result....
    79  	for _, meta := range r.BlockMetas {
    80  		// get a checkpoint to verify from
    81  		res, err := w.Commit(&meta.Header.Height)
    82  		if err != nil {
    83  			return nil, err
    84  		}
    85  		sh := res.SignedHeader
    86  		err = ValidateBlockMeta(meta, sh)
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  	}
    91  
    92  	return r, nil
    93  }
    94  
    95  func (w Wrapper) LatestBlockNumber() (int64, error) {
    96  	info, err := w.BlockchainInfo(0, 0)
    97  	if err != nil {
    98  		return 0, err
    99  	}
   100  	return info.LastHeight, nil
   101  }
   102  
   103  // Block returns an entire block and verifies all signatures
   104  func (w Wrapper) Block(height *int64) (*ctypes.ResultBlock, error) {
   105  	resBlock, err := w.Client.Block(height)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	// get a checkpoint to verify from
   110  	resCommit, err := w.Commit(height)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	sh := resCommit.SignedHeader
   115  
   116  	err = ValidateBlock(resBlock.Block, sh)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	return resBlock, nil
   121  }
   122  
   123  // Commit downloads the Commit and certifies it with the lite.
   124  //
   125  // This is the foundation for all other verification in this module
   126  func (w Wrapper) Commit(height *int64) (*ctypes.ResultCommit, error) {
   127  	if height == nil {
   128  		resStatus, err := w.Client.Status()
   129  		if err != nil {
   130  			return nil, err
   131  		}
   132  		// NOTE: If resStatus.CatchingUp, there is a race
   133  		// condition where the validator set for the next height
   134  		// isn't available until some time after the blockstore
   135  		// has height h on the remote node.  This isn't an issue
   136  		// once the node has caught up, and a syncing node likely
   137  		// won't have this issue esp with the implementation we
   138  		// have here, but we may have to address this at some
   139  		// point.
   140  		height = new(int64)
   141  		*height = resStatus.SyncInfo.LatestBlockHeight
   142  	}
   143  	rpcclient.WaitForHeight(w.Client, *height, nil)
   144  	res, err := w.Client.Commit(height)
   145  	// if we got it, then verify it
   146  	if err == nil {
   147  		sh := res.SignedHeader
   148  		err = w.cert.Verify(sh)
   149  	}
   150  	return res, err
   151  }
   152  
   153  func (w Wrapper) RegisterOpDecoder(typ string, dec merkle.OpDecoder) {
   154  	w.prt.RegisterOpDecoder(typ, dec)
   155  }
   156  
   157  // SubscribeWS subscribes for events using the given query and remote address as
   158  // a subscriber, but does not verify responses (UNSAFE)!
   159  func (w Wrapper) SubscribeWS(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) {
   160  	out, err := w.Client.Subscribe(context.Background(), ctx.RemoteAddr(), query)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	go func() {
   166  		for {
   167  			select {
   168  			case resultEvent := <-out:
   169  				// XXX(melekes) We should have a switch here that performs a validation
   170  				// depending on the event's type.
   171  				ctx.WSConn.TryWriteRPCResponse(
   172  					rpctypes.NewRPCSuccessResponse(
   173  						ctx.WSConn.Codec(),
   174  						ctx.JSONReq.ID,
   175  						resultEvent,
   176  					))
   177  			case <-w.Client.Quit():
   178  				return
   179  			}
   180  		}
   181  	}()
   182  
   183  	return &ctypes.ResultSubscribe{}, nil
   184  }
   185  
   186  // UnsubscribeWS calls original client's Unsubscribe using remote address as a
   187  // subscriber.
   188  func (w Wrapper) UnsubscribeWS(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe, error) {
   189  	err := w.Client.Unsubscribe(context.Background(), ctx.RemoteAddr(), query)
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  	return &ctypes.ResultUnsubscribe{}, nil
   194  }
   195  
   196  // UnsubscribeAllWS calls original client's UnsubscribeAll using remote address
   197  // as a subscriber.
   198  func (w Wrapper) UnsubscribeAllWS(ctx *rpctypes.Context) (*ctypes.ResultUnsubscribe, error) {
   199  	err := w.Client.UnsubscribeAll(context.Background(), ctx.RemoteAddr())
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  	return &ctypes.ResultUnsubscribe{}, nil
   204  }
   205  
   206  // // WrappedSwitch creates a websocket connection that auto-verifies any info
   207  // // coming through before passing it along.
   208  // //
   209  // // Since the verification takes 1-2 rpc calls, this is obviously only for
   210  // // relatively low-throughput situations that can tolerate a bit extra latency
   211  // type WrappedSwitch struct {
   212  // 	types.EventSwitch
   213  // 	client rpcclient.Client
   214  // }
   215  
   216  // // FireEvent verifies any block or header returned from the eventswitch
   217  // func (s WrappedSwitch) FireEvent(event string, data events.EventData) {
   218  // 	tm, ok := data.(types.TMEventData)
   219  // 	if !ok {
   220  // 		fmt.Printf("bad type %#v\n", data)
   221  // 		return
   222  // 	}
   223  
   224  // 	// check to validate it if possible, and drop if not valid
   225  // 	switch t := tm.(type) {
   226  // 	case types.EventDataNewBlockHeader:
   227  // 		err := verifyHeader(s.client, t.Header)
   228  // 		if err != nil {
   229  // 			fmt.Printf("Invalid header: %#v\n", err)
   230  // 			return
   231  // 		}
   232  // 	case types.EventDataNewBlock:
   233  // 		err := verifyBlock(s.client, t.Block)
   234  // 		if err != nil {
   235  // 			fmt.Printf("Invalid block: %#v\n", err)
   236  // 			return
   237  // 		}
   238  // 		// TODO: can we verify tx as well? anything else
   239  // 	}
   240  
   241  // 	// looks good, we fire it
   242  // 	s.EventSwitch.FireEvent(event, data)
   243  // }
   244  
   245  // func verifyHeader(c rpcclient.Client, head *types.Header) error {
   246  // 	// get a checkpoint to verify from
   247  // 	commit, err := c.Commit(&head.Height)
   248  // 	if err != nil {
   249  // 		return err
   250  // 	}
   251  // 	check := certclient.CommitFromResult(commit)
   252  // 	return ValidateHeader(head, check)
   253  // }
   254  //
   255  // func verifyBlock(c rpcclient.Client, block *types.Block) error {
   256  // 	// get a checkpoint to verify from
   257  // 	commit, err := c.Commit(&block.Height)
   258  // 	if err != nil {
   259  // 		return err
   260  // 	}
   261  // 	check := certclient.CommitFromResult(commit)
   262  // 	return ValidateBlock(block, check)
   263  // }