You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

207 lines
4.9 KiB

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
}