github.com/consideritdone/landslidecore@v0.0.0-20230718131026-a8b21c5cf8a7/vm/service.go (about) 1 package vm 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "sort" 8 "time" 9 10 abci "github.com/consideritdone/landslidecore/abci/types" 11 tmbytes "github.com/consideritdone/landslidecore/libs/bytes" 12 tmmath "github.com/consideritdone/landslidecore/libs/math" 13 tmpubsub "github.com/consideritdone/landslidecore/libs/pubsub" 14 tmquery "github.com/consideritdone/landslidecore/libs/pubsub/query" 15 mempl "github.com/consideritdone/landslidecore/mempool" 16 "github.com/consideritdone/landslidecore/p2p" 17 "github.com/consideritdone/landslidecore/proxy" 18 "github.com/consideritdone/landslidecore/rpc/core" 19 ctypes "github.com/consideritdone/landslidecore/rpc/core/types" 20 rpcserver "github.com/consideritdone/landslidecore/rpc/jsonrpc/server" 21 rpctypes "github.com/consideritdone/landslidecore/rpc/jsonrpc/types" 22 blockidxnull "github.com/consideritdone/landslidecore/state/indexer/block/null" 23 "github.com/consideritdone/landslidecore/state/txindex/null" 24 "github.com/consideritdone/landslidecore/types" 25 ) 26 27 var ( 28 SubscribeTimeout = 5 * time.Second 29 _ Service = (*LocalService)(nil) 30 ) 31 32 type ( 33 LocalService struct { 34 vm *VM 35 } 36 37 Service interface { 38 ABCIService 39 EventsService 40 HistoryClient 41 NetworkClient 42 SignClient 43 StatusClient 44 MempoolClient 45 } 46 47 ABCIService interface { 48 // Reading from abci app 49 ABCIInfo(ctx *rpctypes.Context) (*ctypes.ResultABCIInfo, error) 50 ABCIQuery(ctx *rpctypes.Context, path string, data tmbytes.HexBytes, height int64, prove bool) (*ctypes.ResultABCIQuery, error) 51 52 // Writing to abci app 53 BroadcastTxCommit(*rpctypes.Context, types.Tx) (*ctypes.ResultBroadcastTxCommit, error) 54 BroadcastTxAsync(*rpctypes.Context, types.Tx) (*ctypes.ResultBroadcastTx, error) 55 BroadcastTxSync(*rpctypes.Context, types.Tx) (*ctypes.ResultBroadcastTx, error) 56 } 57 58 EventsService interface { 59 Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) 60 Unsubscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe, error) 61 UnsubscribeAll(ctx *rpctypes.Context) (*ctypes.ResultUnsubscribe, error) 62 } 63 64 HistoryClient interface { 65 Genesis(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error) 66 GenesisChunked(*rpctypes.Context, uint) (*ctypes.ResultGenesisChunk, error) 67 BlockchainInfo(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) 68 } 69 70 MempoolClient interface { 71 UnconfirmedTxs(ctx *rpctypes.Context, limit *int) (*ctypes.ResultUnconfirmedTxs, error) 72 NumUnconfirmedTxs(ctx *rpctypes.Context) (*ctypes.ResultUnconfirmedTxs, error) 73 CheckTx(*rpctypes.Context, types.Tx) (*ctypes.ResultCheckTx, error) 74 } 75 76 NetworkClient interface { 77 NetInfo(ctx *rpctypes.Context) (*ctypes.ResultNetInfo, error) 78 DumpConsensusState(ctx *rpctypes.Context) (*ctypes.ResultDumpConsensusState, error) 79 ConsensusState(ctx *rpctypes.Context) (*ctypes.ResultConsensusState, error) 80 ConsensusParams(ctx *rpctypes.Context, height *int64) (*ctypes.ResultConsensusParams, error) 81 Health(ctx *rpctypes.Context) (*ctypes.ResultHealth, error) 82 } 83 84 SignClient interface { 85 Block(ctx *rpctypes.Context, height *int64) (*ctypes.ResultBlock, error) 86 BlockByHash(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) 87 BlockResults(ctx *rpctypes.Context, height *int64) (*ctypes.ResultBlockResults, error) 88 Commit(ctx *rpctypes.Context, height *int64) (*ctypes.ResultCommit, error) 89 Validators(ctx *rpctypes.Context, height *int64, page, perPage *int) (*ctypes.ResultValidators, error) 90 Tx(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) 91 92 TxSearch(ctx *rpctypes.Context, query string, prove bool, 93 page, perPage *int, orderBy string) (*ctypes.ResultTxSearch, error) 94 95 BlockSearch(ctx *rpctypes.Context, query string, 96 page, perPage *int, orderBy string) (*ctypes.ResultBlockSearch, error) 97 } 98 99 StatusClient interface { 100 Status(*rpctypes.Context) (*ctypes.ResultStatus, error) 101 } 102 ) 103 104 func NewService(vm *VM) *LocalService { 105 return &LocalService{vm} 106 } 107 108 func NewServiceAsRPCRoutes(vm *VM) map[string]*rpcserver.RPCFunc { 109 s := NewService(vm) 110 return map[string]*rpcserver.RPCFunc{ 111 // subscribe/unsubscribe are reserved for websocket events. 112 "subscribe": rpcserver.NewWSRPCFunc(s.Subscribe, "query"), 113 "unsubscribe": rpcserver.NewWSRPCFunc(s.Unsubscribe, "query"), 114 "unsubscribe_all": rpcserver.NewWSRPCFunc(s.UnsubscribeAll, ""), 115 116 // info API 117 "health": rpcserver.NewRPCFunc(s.Health, ""), 118 "status": rpcserver.NewRPCFunc(s.Status, ""), 119 "net_info": rpcserver.NewRPCFunc(s.NetInfo, ""), 120 "blockchain": rpcserver.NewRPCFunc(s.BlockchainInfo, "minHeight,maxHeight"), 121 "genesis": rpcserver.NewRPCFunc(s.Genesis, ""), 122 "genesis_chunked": rpcserver.NewRPCFunc(s.GenesisChunked, "chunk"), 123 "block": rpcserver.NewRPCFunc(s.Block, "height"), 124 "block_by_hash": rpcserver.NewRPCFunc(s.BlockByHash, "hash"), 125 "block_results": rpcserver.NewRPCFunc(s.BlockResults, "height"), 126 "commit": rpcserver.NewRPCFunc(s.Commit, "height"), 127 "check_tx": rpcserver.NewRPCFunc(s.CheckTx, "tx"), 128 "tx": rpcserver.NewRPCFunc(s.Tx, "hash,prove"), 129 "tx_search": rpcserver.NewRPCFunc(s.TxSearch, "query,prove,page,per_page,order_by"), 130 "block_search": rpcserver.NewRPCFunc(s.BlockSearch, "query,page,per_page,order_by"), 131 "validators": rpcserver.NewRPCFunc(s.Validators, "height,page,per_page"), 132 "dump_consensus_state": rpcserver.NewRPCFunc(s.DumpConsensusState, ""), 133 "consensus_state": rpcserver.NewRPCFunc(s.ConsensusState, ""), 134 "consensus_params": rpcserver.NewRPCFunc(s.ConsensusParams, "height"), 135 "unconfirmed_txs": rpcserver.NewRPCFunc(s.UnconfirmedTxs, "limit"), 136 "num_unconfirmed_txs": rpcserver.NewRPCFunc(s.NumUnconfirmedTxs, ""), 137 138 // tx broadcast API 139 "broadcast_tx_commit": rpcserver.NewRPCFunc(s.BroadcastTxCommit, "tx"), 140 "broadcast_tx_sync": rpcserver.NewRPCFunc(s.BroadcastTxSync, "tx"), 141 "broadcast_tx_async": rpcserver.NewRPCFunc(s.BroadcastTxAsync, "tx"), 142 143 // abci API 144 "abci_query": rpcserver.NewRPCFunc(s.ABCIQuery, "path,data,height,prove"), 145 "abci_info": rpcserver.NewRPCFunc(s.ABCIInfo, ""), 146 } 147 } 148 149 func (s *LocalService) Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) { 150 addr := ctx.RemoteAddr() 151 152 if s.vm.eventBus.NumClients() >= s.vm.rpcConfig.MaxSubscriptionClients { 153 return nil, fmt.Errorf("max_subscription_clients %d reached", s.vm.rpcConfig.MaxSubscriptionClients) 154 } else if s.vm.eventBus.NumClientSubscriptions(addr) >= s.vm.rpcConfig.MaxSubscriptionsPerClient { 155 return nil, fmt.Errorf("max_subscriptions_per_client %d reached", s.vm.rpcConfig.MaxSubscriptionsPerClient) 156 } 157 158 s.vm.log.Info("Subscribe to query", "remote", addr, "query", query) 159 160 q, err := tmquery.New(query) 161 if err != nil { 162 return nil, fmt.Errorf("failed to parse query: %w", err) 163 } 164 165 subCtx, cancel := context.WithTimeout(ctx.Context(), SubscribeTimeout) 166 defer cancel() 167 168 sub, err := s.vm.eventBus.Subscribe(subCtx, addr, q, s.vm.rpcConfig.SubscriptionBufferSize) 169 if err != nil { 170 return nil, err 171 } 172 173 closeIfSlow := s.vm.rpcConfig.CloseOnSlowClient 174 175 // TODO: inspired by Ilnur: usage of ctx.JSONReq.ID may cause situation when user or server try to create multiple subscriptions with the same id. 176 // Solution: return error code with the error sescription when this situation happens 177 // Capture the current ID, since it can change in the future. 178 subscriptionID := ctx.JSONReq.ID 179 go func() { 180 for { 181 select { 182 case msg := <-sub.Out(): 183 var ( 184 resultEvent = &ctypes.ResultEvent{Query: query, Data: msg.Data(), Events: msg.Events()} 185 resp = rpctypes.NewRPCSuccessResponse(subscriptionID, resultEvent) 186 ) 187 writeCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 188 defer cancel() 189 if err = ctx.WSConn.WriteRPCResponse(writeCtx, resp); err != nil { 190 s.vm.log.Info("Can't write response (slow client)", 191 "to", addr, "subscriptionID", subscriptionID, "err", err) 192 193 if closeIfSlow { 194 var ( 195 err = errors.New("subscription was cancelled (reason: slow client)") 196 resp = rpctypes.RPCServerError(subscriptionID, err) 197 ) 198 if !ctx.WSConn.TryWriteRPCResponse(resp) { 199 s.vm.log.Info("Can't write response (slow client)", 200 "to", addr, "subscriptionID", subscriptionID, "err", err) 201 } 202 return 203 } 204 } 205 case <-sub.Cancelled(): 206 if sub.Err() != tmpubsub.ErrUnsubscribed { 207 var reason string 208 if sub.Err() == nil { 209 reason = "Tendermint exited" 210 } else { 211 reason = sub.Err().Error() 212 } 213 resp := rpctypes.RPCServerError(subscriptionID, err) 214 if !ctx.WSConn.TryWriteRPCResponse(resp) { 215 s.vm.log.Info("Can't write response (slow client)", 216 "to", addr, "subscriptionID", subscriptionID, "err", 217 fmt.Errorf("subscription was cancelled (reason: %s)", reason)) 218 } 219 } 220 return 221 } 222 } 223 }() 224 225 return &ctypes.ResultSubscribe{}, nil 226 } 227 228 func (s *LocalService) Unsubscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe, error) { 229 addr := ctx.RemoteAddr() 230 s.vm.log.Info("Unsubscribe from query", "remote", addr, "query", query) 231 q, err := tmquery.New(query) 232 if err != nil { 233 return nil, fmt.Errorf("failed to parse query: %w", err) 234 } 235 err = s.vm.eventBus.Unsubscribe(context.Background(), addr, q) 236 if err != nil { 237 return nil, err 238 } 239 return &ctypes.ResultUnsubscribe{}, nil 240 } 241 242 func (s *LocalService) UnsubscribeAll(ctx *rpctypes.Context) (*ctypes.ResultUnsubscribe, error) { 243 addr := ctx.RemoteAddr() 244 s.vm.log.Info("Unsubscribe from all", "remote", addr) 245 err := s.vm.eventBus.UnsubscribeAll(context.Background(), addr) 246 if err != nil { 247 return nil, err 248 } 249 return &ctypes.ResultUnsubscribe{}, nil 250 } 251 252 func (s *LocalService) ABCIInfo(ctx *rpctypes.Context) (*ctypes.ResultABCIInfo, error) { 253 resInfo, err := s.vm.app.Query().InfoSync(proxy.RequestInfo) 254 if err != nil || resInfo == nil { 255 return nil, err 256 } 257 return &ctypes.ResultABCIInfo{Response: *resInfo}, nil 258 } 259 260 // TODO: attention! Different signatures in RPC interfaces 261 func (s *LocalService) ABCIQuery( 262 ctx *rpctypes.Context, 263 path string, 264 data tmbytes.HexBytes, 265 height int64, 266 prove bool, 267 ) (*ctypes.ResultABCIQuery, error) { 268 resQuery, err := s.vm.app.Query().QuerySync(abci.RequestQuery{ 269 Path: path, 270 Data: data, 271 Height: height, 272 Prove: prove, 273 }) 274 if err != nil || resQuery == nil { 275 return nil, err 276 } 277 278 return &ctypes.ResultABCIQuery{Response: *resQuery}, nil 279 } 280 281 func (s *LocalService) BroadcastTxCommit(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { 282 subscriber := "" 283 284 // Subscribe to tx being committed in block. 285 subCtx, cancel := context.WithTimeout(ctx.Context(), core.SubscribeTimeout) 286 defer cancel() 287 288 q := types.EventQueryTxFor(tx) 289 deliverTxSub, err := s.vm.eventBus.Subscribe(subCtx, subscriber, q) 290 if err != nil { 291 err = fmt.Errorf("failed to subscribe to tx: %w", err) 292 s.vm.log.Error("Error on broadcast_tx_commit", "err", err) 293 return nil, err 294 } 295 296 defer func() { 297 if err := s.vm.eventBus.Unsubscribe(context.Background(), subscriber, q); err != nil { 298 s.vm.log.Error("Error unsubscribing from eventBus", "err", err) 299 } 300 }() 301 302 // Broadcast tx and wait for CheckTx result 303 checkTxResCh := make(chan *abci.Response, 1) 304 err = s.vm.mempool.CheckTx(tx, func(res *abci.Response) { 305 checkTxResCh <- res 306 }, mempl.TxInfo{}) 307 if err != nil { 308 s.vm.log.Error("Error on broadcastTxCommit", "err", err) 309 return nil, fmt.Errorf("error on broadcastTxCommit: %v", err) 310 } 311 checkTxResMsg := <-checkTxResCh 312 checkTxRes := checkTxResMsg.GetCheckTx() 313 if checkTxRes.Code != abci.CodeTypeOK { 314 return &ctypes.ResultBroadcastTxCommit{ 315 CheckTx: *checkTxRes, 316 DeliverTx: abci.ResponseDeliverTx{}, 317 Hash: tx.Hash(), 318 }, nil 319 } 320 321 // Wait for the tx to be included in a block or timeout. 322 select { 323 case msg := <-deliverTxSub.Out(): // The tx was included in a block. 324 deliverTxRes := msg.Data().(types.EventDataTx) 325 return &ctypes.ResultBroadcastTxCommit{ 326 CheckTx: *checkTxRes, 327 DeliverTx: deliverTxRes.Result, 328 Hash: tx.Hash(), 329 Height: deliverTxRes.Height, 330 }, nil 331 case <-deliverTxSub.Cancelled(): 332 var reason string 333 if deliverTxSub.Err() == nil { 334 reason = "Tendermint exited" 335 } else { 336 reason = deliverTxSub.Err().Error() 337 } 338 err = fmt.Errorf("deliverTxSub was cancelled (reason: %s)", reason) 339 s.vm.log.Error("Error on broadcastTxCommit", "err", err) 340 return &ctypes.ResultBroadcastTxCommit{ 341 CheckTx: *checkTxRes, 342 DeliverTx: abci.ResponseDeliverTx{}, 343 Hash: tx.Hash(), 344 }, err 345 // TODO: use config for timeout 346 case <-time.After(10 * time.Second): 347 err = errors.New("timed out waiting for tx to be included in a block") 348 s.vm.log.Error("Error on broadcastTxCommit", "err", err) 349 return &ctypes.ResultBroadcastTxCommit{ 350 CheckTx: *checkTxRes, 351 DeliverTx: abci.ResponseDeliverTx{}, 352 Hash: tx.Hash(), 353 }, err 354 } 355 } 356 357 func (s *LocalService) BroadcastTxAsync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { 358 err := s.vm.mempool.CheckTx(tx, nil, mempl.TxInfo{}) 359 if err != nil { 360 return nil, err 361 } 362 return &ctypes.ResultBroadcastTx{Hash: tx.Hash()}, nil 363 } 364 365 func (s *LocalService) BroadcastTxSync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { 366 resCh := make(chan *abci.Response, 1) 367 err := s.vm.mempool.CheckTx(tx, func(res *abci.Response) { 368 s.vm.log.With("module", "service").Debug("handled response from checkTx") 369 resCh <- res 370 }, mempl.TxInfo{}) 371 if err != nil { 372 return nil, err 373 } 374 res := <-resCh 375 r := res.GetCheckTx() 376 return &ctypes.ResultBroadcastTx{ 377 Code: r.Code, 378 Data: r.Data, 379 Log: r.Log, 380 Codespace: r.Codespace, 381 Hash: tx.Hash(), 382 }, nil 383 } 384 385 func (s *LocalService) Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) { 386 height, err := getHeight(s.vm.blockStore, heightPtr) 387 if err != nil { 388 return nil, err 389 } 390 391 block := s.vm.blockStore.LoadBlock(height) 392 blockMeta := s.vm.blockStore.LoadBlockMeta(height) 393 if blockMeta == nil { 394 return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: block}, nil 395 } 396 return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil 397 } 398 399 func (s *LocalService) BlockByHash(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) { 400 block := s.vm.blockStore.LoadBlockByHash(hash) 401 if block == nil { 402 return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: nil}, nil 403 } 404 // If block is not nil, then blockMeta can't be nil. 405 blockMeta := s.vm.blockStore.LoadBlockMeta(block.Height) 406 return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil 407 } 408 409 func (s *LocalService) BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) { 410 height, err := getHeight(s.vm.blockStore, heightPtr) 411 if err != nil { 412 return nil, err 413 } 414 415 results, err := s.vm.stateStore.LoadABCIResponses(height) 416 if err != nil { 417 return nil, err 418 } 419 420 return &ctypes.ResultBlockResults{ 421 Height: height, 422 TxsResults: results.DeliverTxs, 423 BeginBlockEvents: results.BeginBlock.Events, 424 EndBlockEvents: results.EndBlock.Events, 425 ValidatorUpdates: results.EndBlock.ValidatorUpdates, 426 ConsensusParamUpdates: results.EndBlock.ConsensusParamUpdates, 427 }, nil 428 } 429 430 func (s *LocalService) Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, error) { 431 height, err := getHeight(s.vm.blockStore, heightPtr) 432 if err != nil { 433 return nil, err 434 } 435 436 blockMeta := s.vm.blockStore.LoadBlockMeta(height) 437 if blockMeta == nil { 438 return nil, nil 439 } 440 header := blockMeta.Header 441 442 // Return the canonical commit (comes from the block at height+1) 443 commit := s.vm.blockStore.LoadBlockCommit(height) 444 return ctypes.NewResultCommit(&header, commit, true), nil 445 } 446 447 func (s *LocalService) Validators(ctx *rpctypes.Context, heightPtr *int64, pagePtr, perPagePtr *int) (*ctypes.ResultValidators, error) { 448 height, err := getHeight(s.vm.blockStore, heightPtr) 449 if err != nil { 450 return nil, err 451 } 452 453 validators, err := s.vm.stateStore.LoadValidators(height) 454 if err != nil { 455 return nil, err 456 } 457 458 totalCount := len(validators.Validators) 459 perPage := validatePerPage(perPagePtr) 460 page, err := validatePage(pagePtr, perPage, totalCount) 461 if err != nil { 462 return nil, err 463 } 464 465 skipCount := validateSkipCount(page, perPage) 466 467 v := validators.Validators[skipCount : skipCount+tmmath.MinInt(perPage, totalCount-skipCount)] 468 469 return &ctypes.ResultValidators{ 470 BlockHeight: height, 471 Validators: v, 472 Count: len(v), 473 Total: totalCount}, nil 474 } 475 476 func (s *LocalService) Tx(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) { 477 if _, ok := s.vm.txIndexer.(*null.TxIndex); ok { 478 return nil, fmt.Errorf("transaction indexing is disabled") 479 } 480 481 r, err := s.vm.txIndexer.Get(hash) 482 if err != nil { 483 return nil, err 484 } 485 486 if r == nil { 487 return nil, fmt.Errorf("tx (%X) not found", hash) 488 } 489 490 height := r.Height 491 index := r.Index 492 493 var proof types.TxProof 494 if prove { 495 block := s.vm.blockStore.LoadBlock(height) 496 proof = block.Data.Txs.Proof(int(index)) // XXX: overflow on 32-bit machines 497 } 498 499 return &ctypes.ResultTx{ 500 Hash: hash, 501 Height: height, 502 Index: index, 503 TxResult: r.Result, 504 Tx: r.Tx, 505 Proof: proof, 506 }, nil 507 } 508 509 func (s *LocalService) TxSearch(ctx *rpctypes.Context, query string, prove bool, pagePtr, perPagePtr *int, orderBy string) (*ctypes.ResultTxSearch, error) { 510 // if index is disabled, return error 511 q, err := tmquery.New(query) 512 if err != nil { 513 return nil, err 514 } 515 516 results, err := s.vm.txIndexer.Search(ctx.Context(), q) 517 if err != nil { 518 return nil, err 519 } 520 521 // sort results (must be done before pagination) 522 switch orderBy { 523 case "desc": 524 sort.Slice(results, func(i, j int) bool { 525 if results[i].Height == results[j].Height { 526 return results[i].Index > results[j].Index 527 } 528 return results[i].Height > results[j].Height 529 }) 530 case "asc", "": 531 sort.Slice(results, func(i, j int) bool { 532 if results[i].Height == results[j].Height { 533 return results[i].Index < results[j].Index 534 } 535 return results[i].Height < results[j].Height 536 }) 537 default: 538 return nil, errors.New("expected order_by to be either `asc` or `desc` or empty") 539 } 540 541 // paginate results 542 totalCount := len(results) 543 perPage := validatePerPage(perPagePtr) 544 545 page, err := validatePage(pagePtr, perPage, totalCount) 546 if err != nil { 547 return nil, err 548 } 549 550 skipCount := validateSkipCount(page, perPage) 551 pageSize := tmmath.MinInt(perPage, totalCount-skipCount) 552 553 apiResults := make([]*ctypes.ResultTx, 0, pageSize) 554 for i := skipCount; i < skipCount+pageSize; i++ { 555 r := results[i] 556 557 var proof types.TxProof 558 if prove { 559 block := s.vm.blockStore.LoadBlock(r.Height) 560 proof = block.Data.Txs.Proof(int(r.Index)) // XXX: overflow on 32-bit machines 561 } 562 563 apiResults = append(apiResults, &ctypes.ResultTx{ 564 Hash: types.Tx(r.Tx).Hash(), 565 Height: r.Height, 566 Index: r.Index, 567 TxResult: r.Result, 568 Tx: r.Tx, 569 Proof: proof, 570 }) 571 } 572 573 return &ctypes.ResultTxSearch{Txs: apiResults, TotalCount: totalCount}, nil 574 } 575 576 // BlockSearch searches for a paginated set of blocks matching BeginBlock and 577 // EndBlock event search criteria. 578 func (s *LocalService) BlockSearch( 579 ctx *rpctypes.Context, 580 query string, 581 pagePtr, perPagePtr *int, 582 orderBy string, 583 ) (*ctypes.ResultBlockSearch, error) { 584 585 // skip if block indexing is disabled 586 if _, ok := s.vm.blockIndexer.(*blockidxnull.BlockerIndexer); ok { 587 return nil, errors.New("block indexing is disabled") 588 } 589 590 q, err := tmquery.New(query) 591 if err != nil { 592 return nil, err 593 } 594 595 results, err := s.vm.blockIndexer.Search(ctx.Context(), q) 596 if err != nil { 597 return nil, err 598 } 599 600 // sort results (must be done before pagination) 601 switch orderBy { 602 case "desc", "": 603 sort.Slice(results, func(i, j int) bool { return results[i] > results[j] }) 604 605 case "asc": 606 sort.Slice(results, func(i, j int) bool { return results[i] < results[j] }) 607 608 default: 609 return nil, errors.New("expected order_by to be either `asc` or `desc` or empty") 610 } 611 612 // paginate results 613 totalCount := len(results) 614 perPage := validatePerPage(perPagePtr) 615 616 page, err := validatePage(pagePtr, perPage, totalCount) 617 if err != nil { 618 return nil, err 619 } 620 621 skipCount := validateSkipCount(page, perPage) 622 pageSize := tmmath.MinInt(perPage, totalCount-skipCount) 623 624 apiResults := make([]*ctypes.ResultBlock, 0, pageSize) 625 for i := skipCount; i < skipCount+pageSize; i++ { 626 block := s.vm.blockStore.LoadBlock(results[i]) 627 if block != nil { 628 blockMeta := s.vm.blockStore.LoadBlockMeta(block.Height) 629 if blockMeta != nil { 630 apiResults = append(apiResults, &ctypes.ResultBlock{ 631 Block: block, 632 BlockID: blockMeta.BlockID, 633 }) 634 } 635 } 636 } 637 638 return &ctypes.ResultBlockSearch{Blocks: apiResults, TotalCount: totalCount}, nil 639 } 640 641 func (s *LocalService) BlockchainInfo(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { 642 // maximum 20 block metas 643 const limit int64 = 20 644 var err error 645 minHeight, maxHeight, err = filterMinMax( 646 s.vm.blockStore.Base(), 647 s.vm.blockStore.Height(), 648 minHeight, 649 maxHeight, 650 limit) 651 if err != nil { 652 return nil, err 653 } 654 s.vm.log.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight) 655 656 var blockMetas []*types.BlockMeta 657 for height := maxHeight; height >= minHeight; height-- { 658 blockMeta := s.vm.blockStore.LoadBlockMeta(height) 659 blockMetas = append(blockMetas, blockMeta) 660 } 661 662 return &ctypes.ResultBlockchainInfo{ 663 LastHeight: s.vm.blockStore.Height(), 664 BlockMetas: blockMetas}, nil 665 } 666 667 func (s *LocalService) Genesis(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error) { 668 //if len(s.vm.genChunks) > 1 { 669 // return nil, errors.New("genesis response is large, please use the genesis_chunked API instead") 670 //} 671 672 return &ctypes.ResultGenesis{Genesis: s.vm.genesis}, nil 673 } 674 675 func (s *LocalService) GenesisChunked(ctx *rpctypes.Context, chunk uint) (*ctypes.ResultGenesisChunk, error) { 676 if s.vm.genChunks == nil { 677 return nil, fmt.Errorf("service configuration error, genesis chunks are not initialized") 678 } 679 680 if len(s.vm.genChunks) == 0 { 681 return nil, fmt.Errorf("service configuration error, there are no chunks") 682 } 683 684 id := int(chunk) 685 686 if id > len(s.vm.genChunks)-1 { 687 return nil, fmt.Errorf("there are %d chunks, %d is invalid", len(s.vm.genChunks)-1, id) 688 } 689 690 return &ctypes.ResultGenesisChunk{ 691 TotalChunks: len(s.vm.genChunks), 692 ChunkNumber: id, 693 Data: s.vm.genChunks[id], 694 }, nil 695 } 696 697 func (s *LocalService) Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) { 698 var ( 699 earliestBlockHeight int64 700 earliestBlockHash tmbytes.HexBytes 701 earliestAppHash tmbytes.HexBytes 702 earliestBlockTimeNano int64 703 ) 704 705 if earliestBlockMeta := s.vm.blockStore.LoadBaseMeta(); earliestBlockMeta != nil { 706 earliestBlockHeight = earliestBlockMeta.Header.Height 707 earliestAppHash = earliestBlockMeta.Header.AppHash 708 earliestBlockHash = earliestBlockMeta.BlockID.Hash 709 earliestBlockTimeNano = earliestBlockMeta.Header.Time.UnixNano() 710 } 711 712 var ( 713 latestBlockHash tmbytes.HexBytes 714 latestAppHash tmbytes.HexBytes 715 latestBlockTimeNano int64 716 717 latestHeight = s.vm.blockStore.Height() 718 ) 719 720 if latestHeight != 0 { 721 if latestBlockMeta := s.vm.blockStore.LoadBlockMeta(latestHeight); latestBlockMeta != nil { 722 latestBlockHash = latestBlockMeta.BlockID.Hash 723 latestAppHash = latestBlockMeta.Header.AppHash 724 latestBlockTimeNano = latestBlockMeta.Header.Time.UnixNano() 725 } 726 } 727 728 result := &ctypes.ResultStatus{ 729 NodeInfo: p2p.DefaultNodeInfo{ 730 DefaultNodeID: p2p.ID(s.vm.chainCtx.NodeID.String()), 731 Network: fmt.Sprintf("%d", s.vm.chainCtx.NetworkID), 732 }, 733 SyncInfo: ctypes.SyncInfo{ 734 LatestBlockHash: latestBlockHash, 735 LatestAppHash: latestAppHash, 736 LatestBlockHeight: latestHeight, 737 LatestBlockTime: time.Unix(0, latestBlockTimeNano), 738 EarliestBlockHash: earliestBlockHash, 739 EarliestAppHash: earliestAppHash, 740 EarliestBlockHeight: earliestBlockHeight, 741 EarliestBlockTime: time.Unix(0, earliestBlockTimeNano), 742 CatchingUp: false, 743 }, 744 ValidatorInfo: ctypes.ValidatorInfo{ 745 Address: proposerPubKey.Address(), 746 PubKey: proposerPubKey, 747 VotingPower: 0, 748 }, 749 } 750 751 return result, nil 752 } 753 754 // ToDo: no peers, no network from tendermint side 755 func (s *LocalService) NetInfo(ctx *rpctypes.Context) (*ctypes.ResultNetInfo, error) { 756 return &ctypes.ResultNetInfo{}, nil 757 } 758 759 // ToDo: we doesn't have consensusState 760 func (s *LocalService) DumpConsensusState(ctx *rpctypes.Context) (*ctypes.ResultDumpConsensusState, error) { 761 return &ctypes.ResultDumpConsensusState{}, nil 762 } 763 764 // ToDo: we doesn't have consensusState 765 func (s *LocalService) ConsensusState(ctx *rpctypes.Context) (*ctypes.ResultConsensusState, error) { 766 return &ctypes.ResultConsensusState{}, nil 767 } 768 769 func (s *LocalService) ConsensusParams(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultConsensusParams, error) { 770 return &ctypes.ResultConsensusParams{ 771 BlockHeight: s.vm.blockStore.Height(), 772 ConsensusParams: *s.vm.genesis.ConsensusParams, 773 }, nil 774 } 775 776 func (s *LocalService) Health(ctx *rpctypes.Context) (*ctypes.ResultHealth, error) { 777 return &ctypes.ResultHealth{}, nil 778 } 779 780 func (s *LocalService) UnconfirmedTxs(ctx *rpctypes.Context, limitPtr *int) (*ctypes.ResultUnconfirmedTxs, error) { 781 limit := validatePerPage(limitPtr) 782 txs := s.vm.mempool.ReapMaxTxs(limit) 783 return &ctypes.ResultUnconfirmedTxs{ 784 Count: len(txs), 785 Total: s.vm.mempool.Size(), 786 Txs: txs, 787 }, nil 788 } 789 790 func (s *LocalService) NumUnconfirmedTxs(ctx *rpctypes.Context) (*ctypes.ResultUnconfirmedTxs, error) { 791 return &ctypes.ResultUnconfirmedTxs{ 792 Count: s.vm.mempool.Size(), 793 Total: s.vm.mempool.Size(), 794 TotalBytes: s.vm.mempool.TxsBytes()}, nil 795 } 796 797 func (s *LocalService) CheckTx(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultCheckTx, error) { 798 res, err := s.vm.app.Mempool().CheckTxSync(abci.RequestCheckTx{Tx: tx}) 799 if err != nil { 800 return nil, err 801 } 802 return &ctypes.ResultCheckTx{ResponseCheckTx: *res}, nil 803 }