github.com/Finschia/finschia-sdk@v0.48.1/baseapp/abci.go (about) 1 package baseapp 2 3 import ( 4 "crypto/sha256" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "os" 9 "sort" 10 "strings" 11 "syscall" 12 "time" 13 14 "github.com/gogo/protobuf/proto" 15 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 16 "google.golang.org/grpc/codes" 17 grpcstatus "google.golang.org/grpc/status" 18 19 abci "github.com/tendermint/tendermint/abci/types" 20 21 "github.com/Finschia/finschia-sdk/codec" 22 snapshottypes "github.com/Finschia/finschia-sdk/snapshots/types" 23 "github.com/Finschia/finschia-sdk/telemetry" 24 sdk "github.com/Finschia/finschia-sdk/types" 25 sdkerrors "github.com/Finschia/finschia-sdk/types/errors" 26 ocabci "github.com/Finschia/ostracon/abci/types" 27 ) 28 29 // InitChain implements the ABCI interface. It runs the initialization logic 30 // directly on the CommitMultiStore. 31 func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) { 32 // On a new chain, we consider the init chain block height as 0, even though 33 // req.InitialHeight is 1 by default. 34 initHeader := tmproto.Header{ChainID: req.ChainId, Time: req.Time} 35 36 // If req.InitialHeight is > 1, then we set the initial version in the 37 // stores. 38 if req.InitialHeight > 1 { 39 app.initialHeight = req.InitialHeight 40 initHeader = tmproto.Header{ChainID: req.ChainId, Height: req.InitialHeight, Time: req.Time} 41 err := app.cms.SetInitialVersion(req.InitialHeight) 42 if err != nil { 43 panic(err) 44 } 45 } 46 47 // initialize the deliver state and check state with a correct header 48 app.setDeliverState(initHeader) 49 app.setCheckState(initHeader) 50 51 // Store the consensus params in the BaseApp's paramstore. Note, this must be 52 // done after the deliver state and context have been set as it's persisted 53 // to state. 54 if req.ConsensusParams != nil { 55 app.StoreConsensusParams(app.deliverState.ctx, req.ConsensusParams) 56 } 57 58 if app.initChainer == nil { 59 return 60 } 61 62 // add block gas meter for any genesis transactions (allow infinite gas) 63 app.deliverState.ctx = app.deliverState.ctx.WithBlockGasMeter(sdk.NewInfiniteGasMeter()) 64 65 res = app.initChainer(app.deliverState.ctx, req) 66 67 // sanity check 68 if len(req.Validators) > 0 { 69 if len(req.Validators) != len(res.Validators) { 70 panic( 71 fmt.Errorf( 72 "len(RequestInitChain.Validators) != len(GenesisValidators) (%d != %d)", 73 len(req.Validators), len(res.Validators), 74 ), 75 ) 76 } 77 78 sort.Sort(abci.ValidatorUpdates(req.Validators)) 79 sort.Sort(abci.ValidatorUpdates(res.Validators)) 80 81 for i := range res.Validators { 82 if !proto.Equal(&res.Validators[i], &req.Validators[i]) { 83 panic(fmt.Errorf("genesisValidators[%d] != req.Validators[%d] ", i, i)) 84 } 85 } 86 } 87 88 // In the case of a new chain, AppHash will be the hash of an empty string. 89 // During an upgrade, it'll be the hash of the last committed block. 90 var appHash []byte 91 if !app.LastCommitID().IsZero() { 92 appHash = app.LastCommitID().Hash 93 } else { 94 // $ echo -n '' | sha256sum 95 // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 96 emptyHash := sha256.Sum256([]byte{}) 97 appHash = emptyHash[:] 98 } 99 100 // NOTE: We don't commit, but BeginBlock for block `initial_height` starts from this 101 // deliverState. 102 return abci.ResponseInitChain{ 103 ConsensusParams: res.ConsensusParams, 104 Validators: res.Validators, 105 AppHash: appHash, 106 } 107 } 108 109 // Info implements the ABCI interface. 110 func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo { 111 lastCommitID := app.cms.LastCommitID() 112 113 return abci.ResponseInfo{ 114 Data: app.name, 115 Version: app.version, 116 AppVersion: app.appVersion, 117 LastBlockHeight: lastCommitID.Version, 118 LastBlockAppHash: lastCommitID.Hash, 119 } 120 } 121 122 // SetOption implements the ABCI interface. 123 func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOption) { 124 return abci.ResponseSetOption{} 125 } 126 127 // BeginBlock implements the ABCI application interface. 128 func (app *BaseApp) BeginBlock(req ocabci.RequestBeginBlock) (res abci.ResponseBeginBlock) { 129 defer telemetry.MeasureSince(time.Now(), "abci", "begin_block") 130 131 if app.cms.TracingEnabled() { 132 app.cms.SetTracingContext(sdk.TraceContext( 133 map[string]interface{}{"blockHeight": req.Header.Height}, 134 )) 135 } 136 137 if err := app.validateHeight(req); err != nil { 138 panic(err) 139 } 140 141 // set the signed validators for addition to context in deliverTx 142 app.voteInfos = req.LastCommitInfo.GetVotes() 143 144 // Initialize the DeliverTx state. If this is the first block, it should 145 // already be initialized in InitChain. Otherwise app.deliverState will be 146 // nil, since it is reset on Commit. 147 if app.deliverState == nil { 148 app.setDeliverState(req.Header) 149 } else { 150 // In the first block, app.deliverState.ctx will already be initialized 151 // by InitChain. Context is now updated with Header information. 152 app.deliverState.ctx = app.deliverState.ctx. 153 WithBlockHeader(req.Header). 154 WithBlockHeight(req.Header.Height) 155 } 156 157 // add block gas meter 158 var gasMeter sdk.GasMeter 159 if maxGas := app.getMaximumBlockGas(app.deliverState.ctx); maxGas > 0 { 160 gasMeter = sdk.NewGasMeter(maxGas) 161 } else { 162 gasMeter = sdk.NewInfiniteGasMeter() 163 } 164 165 // NOTE: header hash is not set in NewContext, so we manually set it here 166 167 app.deliverState.ctx = app.deliverState.ctx. 168 WithVoteInfos(app.voteInfos). 169 WithBlockGasMeter(gasMeter). 170 WithHeaderHash(req.Hash). 171 WithConsensusParams(app.GetConsensusParams(app.deliverState.ctx)) 172 173 // we also set block gas meter to checkState in case the application needs to 174 // verify gas consumption during (Re)CheckTx 175 if app.checkState != nil { 176 app.checkState.ctx = app.checkState.ctx. 177 WithBlockGasMeter(gasMeter). 178 WithHeaderHash(req.Hash) 179 } 180 181 if app.beginBlocker != nil { 182 res = app.beginBlocker(app.deliverState.ctx, req) 183 res.Events = sdk.MarkEventsToIndex(res.Events, app.indexEvents) 184 } 185 // set the signed validators for addition to context in deliverTx 186 app.voteInfos = req.LastCommitInfo.GetVotes() 187 188 // call the hooks with the BeginBlock messages 189 for _, streamingListener := range app.abciListeners { 190 if err := streamingListener.ListenBeginBlock(app.deliverState.ctx, req, res); err != nil { 191 app.logger.Error("BeginBlock listening hook failed", "height", req.Header.Height, "err", err) 192 } 193 } 194 195 return res 196 } 197 198 // EndBlock implements the ABCI interface. 199 func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { 200 defer telemetry.MeasureSince(time.Now(), "abci", "end_block") 201 202 if app.deliverState.ms.TracingEnabled() { 203 app.deliverState.ms = app.deliverState.ms.SetTracingContext(nil).(sdk.CacheMultiStore) 204 } 205 206 if app.endBlocker != nil { 207 res = app.endBlocker(app.deliverState.ctx, req) 208 res.Events = sdk.MarkEventsToIndex(res.Events, app.indexEvents) 209 } 210 211 if cp := app.GetConsensusParams(app.deliverState.ctx); cp != nil { 212 res.ConsensusParamUpdates = cp 213 } 214 215 // call the streaming service hooks with the EndBlock messages 216 for _, streamingListener := range app.abciListeners { 217 if err := streamingListener.ListenEndBlock(app.deliverState.ctx, req, res); err != nil { 218 app.logger.Error("EndBlock listening hook failed", "height", req.Height, "err", err) 219 } 220 } 221 222 return res 223 } 224 225 // CheckTx implements the ABCI interface and executes a tx in CheckTx mode. In 226 // CheckTx mode, messages are not executed. This means messages are only validated 227 // and only the AnteHandler is executed. State is persisted to the BaseApp's 228 // internal CheckTx state if the AnteHandler passes. Otherwise, the ResponseCheckTx 229 // will contain releveant error information. Regardless of tx execution outcome, 230 // the ResponseCheckTx will contain relevant gas execution context. 231 func (app *BaseApp) CheckTxSync(req abci.RequestCheckTx) ocabci.ResponseCheckTx { 232 defer telemetry.MeasureSince(time.Now(), "abci", "check_tx") 233 234 if req.Type != abci.CheckTxType_New && req.Type != abci.CheckTxType_Recheck { 235 panic(fmt.Sprintf("unknown RequestCheckTx type: %s", req.Type)) 236 } 237 238 tx, err := app.preCheckTx(req.Tx) 239 if err != nil { 240 return sdkerrors.ResponseCheckTx(err, 0, 0, app.trace) 241 } 242 243 waits, signals := app.checkAccountWGs.Register(tx) 244 245 app.checkAccountWGs.Wait(waits) 246 defer app.checkAccountWGs.Done(signals) 247 248 gInfo, err := app.checkTx(req.Tx, tx, req.Type == abci.CheckTxType_Recheck) 249 if err != nil { 250 return sdkerrors.ResponseCheckTx(err, gInfo.GasWanted, gInfo.GasUsed, app.trace) 251 // return sdkerrors.ResponseCheckTxWithEvents(err, gInfo.GasWanted, gInfo.GasUsed, anteEvents, app.trace) // TODO(dudong2): need to fix to use ResponseCheckTxWithEvents 252 } 253 254 return ocabci.ResponseCheckTx{ 255 GasWanted: int64(gInfo.GasWanted), // TODO: Should type accept unsigned ints? 256 GasUsed: int64(gInfo.GasUsed), // TODO: Should type accept unsigned ints? 257 } 258 } 259 260 func (app *BaseApp) CheckTxAsync(req abci.RequestCheckTx, callback ocabci.CheckTxCallback) { 261 if req.Type != abci.CheckTxType_New && req.Type != abci.CheckTxType_Recheck { 262 panic(fmt.Sprintf("unknown RequestCheckTx type: %s", req.Type)) 263 } 264 265 reqCheckTx := &RequestCheckTxAsync{ 266 txBytes: req.Tx, 267 recheck: req.Type == abci.CheckTxType_Recheck, 268 callback: callback, 269 prepare: waitGroup1(), 270 } 271 app.chCheckTx <- reqCheckTx 272 273 go app.prepareCheckTx(reqCheckTx) 274 } 275 276 // BeginRecheckTx implements the ABCI interface and set the check state based on the given header 277 func (app *BaseApp) BeginRecheckTx(req ocabci.RequestBeginRecheckTx) ocabci.ResponseBeginRecheckTx { 278 // NOTE: This is safe because Ostracon holds a lock on the mempool for Rechecking. 279 app.setCheckState(req.Header) 280 return ocabci.ResponseBeginRecheckTx{Code: abci.CodeTypeOK} 281 } 282 283 // EndRecheckTx implements the ABCI interface. 284 func (app *BaseApp) EndRecheckTx(req ocabci.RequestEndRecheckTx) ocabci.ResponseEndRecheckTx { 285 return ocabci.ResponseEndRecheckTx{Code: abci.CodeTypeOK} 286 } 287 288 // DeliverTx implements the ABCI interface and executes a tx in DeliverTx mode. 289 // State only gets persisted if all messages are valid and get executed successfully. 290 // Otherwise, the ResponseDeliverTx will contain releveant error information. 291 // Regardless of tx execution outcome, the ResponseDeliverTx will contain relevant 292 // gas execution context. 293 func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) (res abci.ResponseDeliverTx) { 294 defer telemetry.MeasureSince(time.Now(), "abci", "deliver_tx") 295 296 defer func() { 297 for _, streamingListener := range app.abciListeners { 298 if err := streamingListener.ListenDeliverTx(app.deliverState.ctx, req, res); err != nil { 299 app.logger.Error("DeliverTx listening hook failed", "err", err) 300 } 301 } 302 }() 303 304 gInfo := sdk.GasInfo{} 305 resultStr := "successful" 306 307 defer func() { 308 telemetry.IncrCounter(1, "tx", "count") 309 telemetry.IncrCounter(1, "tx", resultStr) 310 telemetry.SetGauge(float32(gInfo.GasUsed), "tx", "gas", "used") 311 telemetry.SetGauge(float32(gInfo.GasWanted), "tx", "gas", "wanted") 312 }() 313 314 tx, err := app.txDecoder(req.Tx) 315 if err != nil { 316 return sdkerrors.ResponseDeliverTx(err, 0, 0, app.trace) 317 } 318 319 gInfo, result, anteEvents, err := app.runTx(req.Tx, tx, false) 320 if err != nil { 321 resultStr = "failed" 322 return sdkerrors.ResponseDeliverTxWithEvents(err, gInfo.GasWanted, gInfo.GasUsed, sdk.MarkEventsToIndex(anteEvents, app.indexEvents), app.trace) 323 } 324 325 return abci.ResponseDeliverTx{ 326 GasWanted: int64(gInfo.GasWanted), // TODO: Should type accept unsigned ints? 327 GasUsed: int64(gInfo.GasUsed), // TODO: Should type accept unsigned ints? 328 Log: result.Log, 329 Data: result.Data, 330 Events: sdk.MarkEventsToIndex(result.Events, app.indexEvents), 331 } 332 } 333 334 // Commit implements the ABCI interface. It will commit all state that exists in 335 // the deliver state's multi-store and includes the resulting commit ID in the 336 // returned abci.ResponseCommit. Commit will reset the deliver state. 337 // Also, if a non-zero halt height is defined in config, Commit will execute 338 // a deferred function call to check against that height and gracefully halt if 339 // it matches the latest committed height. 340 func (app *BaseApp) Commit() (res abci.ResponseCommit) { 341 defer telemetry.MeasureSince(time.Now(), "abci", "commit") 342 343 header := app.deliverState.ctx.BlockHeader() 344 retainHeight := app.GetBlockRetentionHeight(header.Height) 345 346 // Write the DeliverTx state into branched storage and commit the MultiStore. 347 // The write to the DeliverTx state writes all state transitions to the root 348 // MultiStore (app.cms) so when Commit() is called is persists those values. 349 app.deliverState.ms.Write() 350 commitID := app.cms.Commit() 351 app.logger.Info("commit synced", "commit", fmt.Sprintf("%X", commitID)) 352 353 // empty/reset the deliver state 354 app.deliverState = nil 355 356 var halt bool 357 358 switch { 359 case app.haltHeight > 0 && uint64(header.Height) >= app.haltHeight: 360 halt = true 361 362 case app.haltTime > 0 && header.Time.Unix() >= int64(app.haltTime): 363 halt = true 364 } 365 366 if halt { 367 // Halt the binary and allow Tendermint to receive the ResponseCommit 368 // response with the commit ID hash. This will allow the node to successfully 369 // restart and process blocks assuming the halt configuration has been 370 // reset or moved to a more distant value. 371 app.halt() 372 } 373 374 if app.snapshotInterval > 0 && uint64(header.Height)%app.snapshotInterval == 0 { 375 go app.snapshot(header.Height) 376 } 377 378 return abci.ResponseCommit{ 379 Data: commitID.Hash, 380 RetainHeight: retainHeight, 381 } 382 } 383 384 // halt attempts to gracefully shutdown the node via SIGINT and SIGTERM falling 385 // back on os.Exit if both fail. 386 func (app *BaseApp) halt() { 387 app.logger.Info("halting node per configuration", "height", app.haltHeight, "time", app.haltTime) 388 389 p, err := os.FindProcess(os.Getpid()) 390 if err == nil { 391 // attempt cascading signals in case SIGINT fails (os dependent) 392 sigIntErr := p.Signal(syscall.SIGINT) 393 sigTermErr := p.Signal(syscall.SIGTERM) 394 395 if sigIntErr == nil || sigTermErr == nil { 396 return 397 } 398 } 399 400 // Resort to exiting immediately if the process could not be found or killed 401 // via SIGINT/SIGTERM signals. 402 app.logger.Info("failed to send SIGINT/SIGTERM; exiting...") 403 os.Exit(0) 404 } 405 406 // snapshot takes a snapshot of the current state and prunes any old snapshottypes. 407 func (app *BaseApp) snapshot(height int64) { 408 if app.snapshotManager == nil { 409 app.logger.Info("snapshot manager not configured") 410 return 411 } 412 413 app.logger.Info("creating state snapshot", "height", height) 414 415 snapshot, err := app.snapshotManager.Create(uint64(height)) 416 if err != nil { 417 app.logger.Error("failed to create state snapshot", "height", height, "err", err) 418 return 419 } 420 421 app.logger.Info("completed state snapshot", "height", height, "format", snapshot.Format) 422 423 if app.snapshotKeepRecent > 0 { 424 app.logger.Debug("pruning state snapshots") 425 426 pruned, err := app.snapshotManager.Prune(app.snapshotKeepRecent) 427 if err != nil { 428 app.logger.Error("Failed to prune state snapshots", "err", err) 429 return 430 } 431 432 app.logger.Debug("pruned state snapshots", "pruned", pruned) 433 } 434 } 435 436 // Query implements the ABCI interface. It delegates to CommitMultiStore if it 437 // implements Queryable. 438 func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { 439 defer telemetry.MeasureSince(time.Now(), "abci", "query") 440 441 // Add panic recovery for all queries. 442 // ref: https://github.com/cosmos/cosmos-sdk/pull/8039 443 defer func() { 444 if r := recover(); r != nil { 445 res = sdkerrors.QueryResultWithDebug(sdkerrors.Wrapf(sdkerrors.ErrPanic, "%v", r), app.trace) 446 } 447 }() 448 449 // when a client did not provide a query height, manually inject the latest 450 if req.Height == 0 { 451 req.Height = app.LastBlockHeight() 452 } 453 454 // handle gRPC routes first rather than calling splitPath because '/' characters 455 // are used as part of gRPC paths 456 if grpcHandler := app.grpcQueryRouter.Route(req.Path); grpcHandler != nil { 457 return app.handleQueryGRPC(grpcHandler, req) 458 } 459 460 path := splitPath(req.Path) 461 if len(path) == 0 { 462 return sdkerrors.QueryResultWithDebug(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "no query path provided"), app.trace) 463 } 464 465 switch path[0] { 466 // "/app" prefix for special application queries 467 case "app": 468 return handleQueryApp(app, path, req) 469 470 case "store": 471 return handleQueryStore(app, path, req) 472 473 case "p2p": 474 return handleQueryP2P(app, path) 475 476 case "custom": 477 return handleQueryCustom(app, path, req) 478 } 479 480 return sdkerrors.QueryResultWithDebug(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown query path"), app.trace) 481 } 482 483 // ListSnapshots implements the ABCI interface. It delegates to app.snapshotManager if set. 484 func (app *BaseApp) ListSnapshots(req abci.RequestListSnapshots) abci.ResponseListSnapshots { 485 resp := abci.ResponseListSnapshots{Snapshots: []*abci.Snapshot{}} 486 if app.snapshotManager == nil { 487 return resp 488 } 489 490 snapshots, err := app.snapshotManager.List() 491 if err != nil { 492 app.logger.Error("failed to list snapshots", "err", err) 493 return resp 494 } 495 496 for _, snapshot := range snapshots { 497 abciSnapshot, err := snapshot.ToABCI() 498 if err != nil { 499 app.logger.Error("failed to list snapshots", "err", err) 500 return resp 501 } 502 resp.Snapshots = append(resp.Snapshots, &abciSnapshot) 503 } 504 505 return resp 506 } 507 508 // LoadSnapshotChunk implements the ABCI interface. It delegates to app.snapshotManager if set. 509 func (app *BaseApp) LoadSnapshotChunk(req abci.RequestLoadSnapshotChunk) abci.ResponseLoadSnapshotChunk { 510 if app.snapshotManager == nil { 511 return abci.ResponseLoadSnapshotChunk{} 512 } 513 chunk, err := app.snapshotManager.LoadChunk(req.Height, req.Format, req.Chunk) 514 if err != nil { 515 app.logger.Error( 516 "failed to load snapshot chunk", 517 "height", req.Height, 518 "format", req.Format, 519 "chunk", req.Chunk, 520 "err", err, 521 ) 522 return abci.ResponseLoadSnapshotChunk{} 523 } 524 return abci.ResponseLoadSnapshotChunk{Chunk: chunk} 525 } 526 527 // OfferSnapshot implements the ABCI interface. It delegates to app.snapshotManager if set. 528 func (app *BaseApp) OfferSnapshot(req abci.RequestOfferSnapshot) abci.ResponseOfferSnapshot { 529 if app.snapshotManager == nil { 530 app.logger.Error("snapshot manager not configured") 531 return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ABORT} 532 } 533 534 if req.Snapshot == nil { 535 app.logger.Error("received nil snapshot") 536 return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT} 537 } 538 539 snapshot, err := snapshottypes.SnapshotFromABCI(req.Snapshot) 540 if err != nil { 541 app.logger.Error("failed to decode snapshot metadata", "err", err) 542 return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT} 543 } 544 545 err = app.snapshotManager.Restore(snapshot) 546 switch { 547 case err == nil: 548 return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT} 549 550 case errors.Is(err, snapshottypes.ErrUnknownFormat): 551 return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT_FORMAT} 552 553 case errors.Is(err, snapshottypes.ErrInvalidMetadata): 554 app.logger.Error( 555 "rejecting invalid snapshot", 556 "height", req.Snapshot.Height, 557 "format", req.Snapshot.Format, 558 "err", err, 559 ) 560 return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT} 561 562 default: 563 app.logger.Error( 564 "failed to restore snapshot", 565 "height", req.Snapshot.Height, 566 "format", req.Snapshot.Format, 567 "err", err, 568 ) 569 570 // We currently don't support resetting the IAVL stores and retrying a different snapshot, 571 // so we ask Tendermint to abort all snapshot restoration. 572 return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ABORT} 573 } 574 } 575 576 // ApplySnapshotChunk implements the ABCI interface. It delegates to app.snapshotManager if set. 577 func (app *BaseApp) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) abci.ResponseApplySnapshotChunk { 578 if app.snapshotManager == nil { 579 app.logger.Error("snapshot manager not configured") 580 return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ABORT} 581 } 582 583 _, err := app.snapshotManager.RestoreChunk(req.Chunk) 584 switch { 585 case err == nil: 586 return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT} 587 588 case errors.Is(err, snapshottypes.ErrChunkHashMismatch): 589 app.logger.Error( 590 "chunk checksum mismatch; rejecting sender and requesting refetch", 591 "chunk", req.Index, 592 "sender", req.Sender, 593 "err", err, 594 ) 595 return abci.ResponseApplySnapshotChunk{ 596 Result: abci.ResponseApplySnapshotChunk_RETRY, 597 RefetchChunks: []uint32{req.Index}, 598 RejectSenders: []string{req.Sender}, 599 } 600 601 default: 602 app.logger.Error("failed to restore snapshot", "err", err) 603 return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ABORT} 604 } 605 } 606 607 func (app *BaseApp) handleQueryGRPC(handler GRPCQueryHandler, req abci.RequestQuery) abci.ResponseQuery { 608 ctx, err := app.createQueryContext(req.Height, req.Prove) 609 if err != nil { 610 return sdkerrors.QueryResultWithDebug(err, app.trace) 611 } 612 613 res, err := handler(ctx, req) 614 if err != nil { 615 res = sdkerrors.QueryResultWithDebug(gRPCErrorToSDKError(err), app.trace) 616 res.Height = req.Height 617 return res 618 } 619 620 return res 621 } 622 623 func gRPCErrorToSDKError(err error) error { 624 status, ok := grpcstatus.FromError(err) 625 if !ok { 626 return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error()) 627 } 628 629 switch status.Code() { 630 case codes.NotFound: 631 return sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, err.Error()) 632 case codes.InvalidArgument: 633 return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error()) 634 case codes.FailedPrecondition: 635 return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error()) 636 case codes.Unauthenticated: 637 return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, err.Error()) 638 default: 639 return sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, err.Error()) 640 } 641 } 642 643 func checkNegativeHeight(height int64) error { 644 if height < 0 { 645 // Reject invalid heights. 646 return sdkerrors.Wrap( 647 sdkerrors.ErrInvalidRequest, 648 "cannot query with height < 0; please provide a valid height", 649 ) 650 } 651 return nil 652 } 653 654 // createQueryContext creates a new sdk.Context for a query, taking as args 655 // the block height and whether the query needs a proof or not. 656 func (app *BaseApp) createQueryContext(height int64, prove bool) (sdk.Context, error) { 657 if err := checkNegativeHeight(height); err != nil { 658 return sdk.Context{}, err 659 } 660 661 lastBlockHeight := app.LastBlockHeight() 662 if height > lastBlockHeight { 663 return sdk.Context{}, 664 sdkerrors.Wrap( 665 sdkerrors.ErrInvalidHeight, 666 "cannot query with height in the future; please provide a valid height", 667 ) 668 } 669 670 // when a client did not provide a query height, manually inject the latest 671 if height == 0 { 672 height = lastBlockHeight 673 } 674 675 if height <= 1 && prove { 676 return sdk.Context{}, 677 sdkerrors.Wrap( 678 sdkerrors.ErrInvalidRequest, 679 "cannot query with proof when height <= 1; please provide a valid height", 680 ) 681 } 682 683 cacheMS, err := app.cms.CacheMultiStoreWithVersion(height) 684 if err != nil { 685 return sdk.Context{}, 686 sdkerrors.Wrapf( 687 sdkerrors.ErrInvalidRequest, 688 "failed to load state at height %d; %s (latest height: %d)", height, err, lastBlockHeight, 689 ) 690 } 691 692 // branch the commit-multistore for safety 693 app.checkStateMtx.RLock() 694 ctx := sdk.NewContext( 695 cacheMS, app.checkState.ctx.BlockHeader(), true, app.logger, 696 ).WithMinGasPrices(app.minGasPrices).WithBlockHeight(height) 697 app.checkStateMtx.RUnlock() 698 699 return ctx, nil 700 } 701 702 // GetBlockRetentionHeight returns the height for which all blocks below this height 703 // are pruned from Tendermint. Given a commitment height and a non-zero local 704 // minRetainBlocks configuration, the retentionHeight is the smallest height that 705 // satisfies: 706 // 707 // - Unbonding (safety threshold) time: The block interval in which validators 708 // can be economically punished for misbehavior. Blocks in this interval must be 709 // auditable e.g. by the light client. 710 // 711 // - Logical store snapshot interval: The block interval at which the underlying 712 // logical store database is persisted to disk, e.g. every 10000 heights. Blocks 713 // since the last IAVL snapshot must be available for replay on application restart. 714 // 715 // - State sync snapshots: Blocks since the oldest available snapshot must be 716 // available for state sync nodes to catch up (oldest because a node may be 717 // restoring an old snapshot while a new snapshot was taken). 718 // 719 // - Local (minRetainBlocks) config: Archive nodes may want to retain more or 720 // all blocks, e.g. via a local config option min-retain-blocks. There may also 721 // be a need to vary retention for other nodes, e.g. sentry nodes which do not 722 // need historical blocks. 723 func (app *BaseApp) GetBlockRetentionHeight(commitHeight int64) int64 { 724 // pruning is disabled if minRetainBlocks is zero 725 if app.minRetainBlocks == 0 { 726 return 0 727 } 728 729 minNonZero := func(x, y int64) int64 { 730 switch { 731 case x == 0: 732 return y 733 case y == 0: 734 return x 735 case x < y: 736 return x 737 default: 738 return y 739 } 740 } 741 742 // Define retentionHeight as the minimum value that satisfies all non-zero 743 // constraints. All blocks below (commitHeight-retentionHeight) are pruned 744 // from Tendermint. 745 var retentionHeight int64 746 747 // Define the number of blocks needed to protect against misbehaving validators 748 // which allows light clients to operate safely. Note, we piggy back of the 749 // evidence parameters instead of computing an estimated nubmer of blocks based 750 // on the unbonding period and block commitment time as the two should be 751 // equivalent. 752 cp := app.GetConsensusParams(app.deliverState.ctx) 753 if cp != nil && cp.Evidence != nil && cp.Evidence.MaxAgeNumBlocks > 0 { 754 retentionHeight = commitHeight - cp.Evidence.MaxAgeNumBlocks 755 } 756 757 // Define the state pruning offset, i.e. the block offset at which the 758 // underlying logical database is persisted to disk. 759 statePruningOffset := int64(app.cms.GetPruning().KeepEvery) 760 if statePruningOffset > 0 { 761 if commitHeight > statePruningOffset { 762 v := commitHeight - (commitHeight % statePruningOffset) 763 retentionHeight = minNonZero(retentionHeight, v) 764 } else { 765 // Hitting this case means we have persisting enabled but have yet to reach 766 // a height in which we persist state, so we return zero regardless of other 767 // conditions. Otherwise, we could end up pruning blocks without having 768 // any state committed to disk. 769 return 0 770 } 771 } 772 773 if app.snapshotInterval > 0 && app.snapshotKeepRecent > 0 { 774 v := commitHeight - int64((app.snapshotInterval * uint64(app.snapshotKeepRecent))) 775 retentionHeight = minNonZero(retentionHeight, v) 776 } 777 778 v := commitHeight - int64(app.minRetainBlocks) 779 retentionHeight = minNonZero(retentionHeight, v) 780 781 if retentionHeight <= 0 { 782 // prune nothing in the case of a non-positive height 783 return 0 784 } 785 786 return retentionHeight 787 } 788 789 func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery { 790 if len(path) >= 2 { 791 switch path[1] { 792 case "simulate": 793 txBytes := req.Data 794 795 gInfo, res, err := app.Simulate(txBytes) 796 if err != nil { 797 return sdkerrors.QueryResultWithDebug(sdkerrors.Wrap(err, "failed to simulate tx"), app.trace) 798 } 799 800 simRes := &sdk.SimulationResponse{ 801 GasInfo: gInfo, 802 Result: res, 803 } 804 805 bz, err := codec.ProtoMarshalJSON(simRes, app.interfaceRegistry) 806 if err != nil { 807 return sdkerrors.QueryResultWithDebug(sdkerrors.Wrap(err, "failed to JSON encode simulation response"), app.trace) 808 } 809 810 return abci.ResponseQuery{ 811 Codespace: sdkerrors.RootCodespace, 812 Height: req.Height, 813 Value: bz, 814 } 815 816 case "version": 817 return abci.ResponseQuery{ 818 Codespace: sdkerrors.RootCodespace, 819 Height: req.Height, 820 Value: []byte(app.version), 821 } 822 823 case "snapshots": 824 var responseValue []byte 825 826 response := app.ListSnapshots(abci.RequestListSnapshots{}) 827 828 responseValue, err := json.Marshal(response) 829 if err != nil { 830 sdkerrors.QueryResult(sdkerrors.Wrap(err, fmt.Sprintf("failed to marshal list snapshots response %v", response))) 831 } 832 833 return abci.ResponseQuery{ 834 Codespace: sdkerrors.RootCodespace, 835 Height: req.Height, 836 Value: responseValue, 837 } 838 839 default: 840 return sdkerrors.QueryResultWithDebug(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown query: %s", path), app.trace) 841 } 842 } 843 844 return sdkerrors.QueryResultWithDebug( 845 sdkerrors.Wrap( 846 sdkerrors.ErrUnknownRequest, 847 "expected second parameter to be either 'simulate' or 'version', neither was present", 848 ), app.trace) 849 } 850 851 func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery { 852 // "/store" prefix for store queries 853 queryable, ok := app.cms.(sdk.Queryable) 854 if !ok { 855 return sdkerrors.QueryResultWithDebug(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "multistore doesn't support queries"), app.trace) 856 } 857 858 req.Path = "/" + strings.Join(path[1:], "/") 859 860 if req.Height <= 1 && req.Prove { 861 return sdkerrors.QueryResultWithDebug( 862 sdkerrors.Wrap( 863 sdkerrors.ErrInvalidRequest, 864 "cannot query with proof when height <= 1; please provide a valid height", 865 ), app.trace) 866 } 867 868 resp := queryable.Query(req) 869 resp.Height = req.Height 870 871 return resp 872 } 873 874 func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery { 875 // path[0] should be "custom" because "/custom" prefix is required for keeper 876 // queries. 877 // 878 // The QueryRouter routes using path[1]. For example, in the path 879 // "custom/gov/proposal", QueryRouter routes using "gov". 880 if len(path) < 2 || path[1] == "" { 881 return sdkerrors.QueryResultWithDebug(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "no route for custom query specified"), app.trace) 882 } 883 884 querier := app.queryRouter.Route(path[1]) 885 if querier == nil { 886 return sdkerrors.QueryResultWithDebug(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "no custom querier found for route %s", path[1]), app.trace) 887 } 888 889 ctx, err := app.createQueryContext(req.Height, req.Prove) 890 if err != nil { 891 return sdkerrors.QueryResultWithDebug(err, app.trace) 892 } 893 894 // Passes the rest of the path as an argument to the querier. 895 // 896 // For example, in the path "custom/gov/proposal/test", the gov querier gets 897 // []string{"proposal", "test"} as the path. 898 resBytes, err := querier(ctx, path[2:], req) 899 if err != nil { 900 res := sdkerrors.QueryResultWithDebug(err, app.trace) 901 res.Height = req.Height 902 return res 903 } 904 905 return abci.ResponseQuery{ 906 Height: req.Height, 907 Value: resBytes, 908 } 909 } 910 911 // splitPath splits a string path using the delimiter '/'. 912 // 913 // e.g. "this/is/funny" becomes []string{"this", "is", "funny"} 914 func splitPath(requestPath string) (path []string) { 915 path = strings.Split(requestPath, "/") 916 917 // first element is empty string 918 if len(path) > 0 && path[0] == "" { 919 path = path[1:] 920 } 921 922 return path 923 } 924 925 // createQueryContext creates a new sdk.Context for a query, taking as args 926 // the block height and whether the query needs a proof or not. 927 func (app *BaseApp) createQueryContextWithCheckState() sdk.Context { 928 929 cacheMS := app.checkState.CacheMultiStore() 930 931 // branch the commit-multistore for safety 932 app.checkStateMtx.RLock() 933 ctx := sdk.NewContext( 934 cacheMS, app.checkState.ctx.BlockHeader(), true, app.logger, 935 ).WithMinGasPrices(app.minGasPrices).WithBlockHeight(app.LastBlockHeight()) 936 app.checkStateMtx.RUnlock() 937 938 return ctx 939 }