package core import ( "encoding/base64" "fmt" "time" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/crypto" tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/state/indexer" "github.com/tendermint/tendermint/state/txindex" "github.com/tendermint/tendermint/types" ) const ( // see README defaultPerPage = 30 maxPerPage = 100 // SubscribeTimeout is the maximum time we wait to subscribe for an event. // must be less than the server's write timeout (see rpcserver.DefaultConfig) SubscribeTimeout = 5 * time.Second // genesisChunkSize is the maximum size, in bytes, of each // chunk in the genesis structure for the chunked API genesisChunkSize = 16 * 1024 * 1024 // 16 ) var ( // set by Node env *Environment ) // SetEnvironment sets up the given Environment. // It will race if multiple Node call SetEnvironment. func SetEnvironment(e *Environment) { env = e } //---------------------------------------------- // These interfaces are used by RPC and must be thread safe type Consensus interface { GetState() sm.State GetValidators() (int64, []*types.Validator) GetLastHeight() int64 GetRoundStateJSON() ([]byte, error) GetRoundStateSimpleJSON() ([]byte, error) } type transport interface { Listeners() []string IsListening() bool NodeInfo() p2p.NodeInfo } type peers interface { AddPersistentPeers([]string) error AddUnconditionalPeerIDs([]string) error AddPrivatePeerIDs([]string) error DialPeersAsync([]string) error Peers() p2p.IPeerSet } //---------------------------------------------- // Environment contains objects and interfaces used by the RPC. It is expected // to be setup once during startup. type Environment struct { // external, thread safe interfaces ProxyAppQuery proxy.AppConnQuery ProxyAppMempool proxy.AppConnMempool // interfaces defined in types and above StateStore sm.Store BlockStore sm.BlockStore EvidencePool sm.EvidencePool ConsensusState Consensus P2PPeers peers P2PTransport transport // objects PubKey crypto.PubKey GenDoc *types.GenesisDoc // cache the genesis structure TxIndexer txindex.TxIndexer BlockIndexer indexer.BlockIndexer ConsensusReactor *consensus.Reactor EventBus *types.EventBus // thread safe Mempool mempl.Mempool Logger log.Logger Config cfg.RPCConfig // cache of chunked genesis data. genChunks []string } //---------------------------------------------- func validatePage(pagePtr *int, perPage, totalCount int) (int, error) { if perPage < 1 { panic(fmt.Sprintf("zero or negative perPage: %d", perPage)) } if pagePtr == nil { // no page parameter return 1, nil } pages := ((totalCount - 1) / perPage) + 1 if pages == 0 { pages = 1 // one page (even if it's empty) } page := *pagePtr if page <= 0 || page > pages { return 1, fmt.Errorf("page should be within [1, %d] range, given %d", pages, page) } return page, nil } func validatePerPage(perPagePtr *int) int { if perPagePtr == nil { // no per_page parameter return defaultPerPage } perPage := *perPagePtr if perPage < 1 { return defaultPerPage } else if perPage > maxPerPage { return maxPerPage } return perPage } // InitGenesisChunks configures the environment and should be called on service // startup. func InitGenesisChunks() error { if env.genChunks != nil { return nil } if env.GenDoc == nil { return nil } data, err := tmjson.Marshal(env.GenDoc) if err != nil { return err } for i := 0; i < len(data); i += genesisChunkSize { end := i + genesisChunkSize if end > len(data) { end = len(data) } env.genChunks = append(env.genChunks, base64.StdEncoding.EncodeToString(data[i:end])) } return nil } func validateSkipCount(page, perPage int) int { skipCount := (page - 1) * perPage if skipCount < 0 { return 0 } return skipCount } // latestHeight can be either latest committed or uncommitted (+1) height. func getHeight(latestHeight int64, heightPtr *int64) (int64, error) { if heightPtr != nil { height := *heightPtr if height <= 0 { return 0, fmt.Errorf("height must be greater than 0, but got %d", height) } if height > latestHeight { return 0, fmt.Errorf("height %d must be less than or equal to the current blockchain height %d", height, latestHeight) } base := env.BlockStore.Base() if height < base { return 0, fmt.Errorf("height %d is not available, lowest height is %d", height, base) } return height, nil } return latestHeight, nil } func latestUncommittedHeight() int64 { nodeIsSyncing := env.ConsensusReactor.WaitSync() if nodeIsSyncing { return env.BlockStore.Height() } return env.BlockStore.Height() + 1 }