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