github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/rpc/core/env.go (about) 1 package core 2 3 import ( 4 "encoding/base64" 5 "fmt" 6 "sync" 7 "time" 8 9 cfg "github.com/badrootd/celestia-core/config" 10 "github.com/badrootd/celestia-core/consensus" 11 "github.com/badrootd/celestia-core/crypto" 12 cmtjson "github.com/badrootd/celestia-core/libs/json" 13 "github.com/badrootd/celestia-core/libs/log" 14 mempl "github.com/badrootd/celestia-core/mempool" 15 "github.com/badrootd/celestia-core/p2p" 16 "github.com/badrootd/celestia-core/proxy" 17 sm "github.com/badrootd/celestia-core/state" 18 "github.com/badrootd/celestia-core/state/indexer" 19 "github.com/badrootd/celestia-core/state/txindex" 20 "github.com/badrootd/celestia-core/types" 21 ) 22 23 const ( 24 // see README 25 defaultPerPage = 30 26 maxPerPage = 100 27 28 // SubscribeTimeout is the maximum time we wait to subscribe for an event. 29 // must be less than the server's write timeout (see rpcserver.DefaultConfig) 30 SubscribeTimeout = 5 * time.Second 31 32 // genesisChunkSize is the maximum size, in bytes, of each 33 // chunk in the genesis structure for the chunked API 34 genesisChunkSize = 16 * 1024 * 1024 // 16 35 ) 36 37 var ( 38 // set by Node 39 mut = &sync.Mutex{} 40 globalEnv *Environment 41 ) 42 43 // SetEnvironment sets the global environment to e. The globalEnv var that this 44 // function modifies is protected by a sync.Once so multiple calls within the 45 // same process will not be effective. 46 func SetEnvironment(e *Environment) { 47 mut.Lock() 48 defer mut.Unlock() 49 globalEnv = e 50 } 51 52 func GetEnvironment() *Environment { 53 mut.Lock() 54 defer mut.Unlock() 55 return globalEnv 56 } 57 58 //---------------------------------------------- 59 // These interfaces are used by RPC and must be thread safe 60 61 type Consensus interface { 62 GetState() sm.State 63 GetValidators() (int64, []*types.Validator) 64 GetLastHeight() int64 65 GetRoundStateJSON() ([]byte, error) 66 GetRoundStateSimpleJSON() ([]byte, error) 67 } 68 69 type transport interface { 70 Listeners() []string 71 IsListening() bool 72 NodeInfo() p2p.NodeInfo 73 } 74 75 type peers interface { 76 AddPersistentPeers([]string) error 77 AddUnconditionalPeerIDs([]string) error 78 AddPrivatePeerIDs([]string) error 79 DialPeersAsync([]string) error 80 Peers() p2p.IPeerSet 81 } 82 83 // ---------------------------------------------- 84 // Environment contains objects and interfaces used by the RPC. It is expected 85 // to be setup once during startup. 86 type Environment struct { 87 // external, thread safe interfaces 88 ProxyAppQuery proxy.AppConnQuery 89 ProxyAppMempool proxy.AppConnMempool 90 91 // interfaces defined in types and above 92 StateStore sm.Store 93 BlockStore sm.BlockStore 94 EvidencePool sm.EvidencePool 95 ConsensusState Consensus 96 P2PPeers peers 97 P2PTransport transport 98 99 // objects 100 PubKey crypto.PubKey 101 GenDoc *types.GenesisDoc // cache the genesis structure 102 TxIndexer txindex.TxIndexer 103 BlockIndexer indexer.BlockIndexer 104 ConsensusReactor *consensus.Reactor 105 EventBus *types.EventBus // thread safe 106 Mempool mempl.Mempool 107 108 Logger log.Logger 109 110 Config cfg.RPCConfig 111 112 // cache of chunked genesis data. 113 genChunks []string 114 } 115 116 //---------------------------------------------- 117 118 func validatePage(pagePtr *int, perPage, totalCount int) (int, error) { 119 if perPage < 1 { 120 panic(fmt.Sprintf("zero or negative perPage: %d", perPage)) 121 } 122 123 if pagePtr == nil { // no page parameter 124 return 1, nil 125 } 126 127 pages := ((totalCount - 1) / perPage) + 1 128 if pages == 0 { 129 pages = 1 // one page (even if it's empty) 130 } 131 page := *pagePtr 132 if page <= 0 || page > pages { 133 return 1, fmt.Errorf("page should be within [1, %d] range, given %d", pages, page) 134 } 135 136 return page, nil 137 } 138 139 func validatePerPage(perPagePtr *int) int { 140 if perPagePtr == nil { // no per_page parameter 141 return defaultPerPage 142 } 143 144 perPage := *perPagePtr 145 if perPage < 1 { 146 return defaultPerPage 147 } else if perPage > maxPerPage { 148 return maxPerPage 149 } 150 return perPage 151 } 152 153 // InitGenesisChunks configures the environment and should be called on service 154 // startup. 155 func InitGenesisChunks() error { 156 if GetEnvironment().genChunks != nil { 157 return nil 158 } 159 160 if GetEnvironment().GenDoc == nil { 161 return nil 162 } 163 164 data, err := cmtjson.Marshal(GetEnvironment().GenDoc) 165 if err != nil { 166 return err 167 } 168 mut.Lock() 169 defer mut.Unlock() 170 for i := 0; i < len(data); i += genesisChunkSize { 171 end := i + genesisChunkSize 172 173 if end > len(data) { 174 end = len(data) 175 } 176 177 globalEnv.genChunks = append(globalEnv.genChunks, base64.StdEncoding.EncodeToString(data[i:end])) 178 } 179 180 return nil 181 } 182 183 func validateSkipCount(page, perPage int) int { 184 skipCount := (page - 1) * perPage 185 if skipCount < 0 { 186 return 0 187 } 188 189 return skipCount 190 } 191 192 // latestHeight can be either latest committed or uncommitted (+1) height. 193 func getHeight(latestHeight int64, heightPtr *int64) (int64, error) { 194 if heightPtr != nil { 195 height := *heightPtr 196 if height <= 0 { 197 return 0, fmt.Errorf("height must be greater than 0, but got %d", height) 198 } 199 if height > latestHeight { 200 return 0, fmt.Errorf("height %d must be less than or equal to the current blockchain height %d", 201 height, latestHeight) 202 } 203 base := GetEnvironment().BlockStore.Base() 204 if height < base { 205 return 0, fmt.Errorf("height %d is not available, lowest height is %d", 206 height, base) 207 } 208 return height, nil 209 } 210 return latestHeight, nil 211 } 212 213 func latestUncommittedHeight() int64 { 214 env := GetEnvironment() 215 nodeIsSyncing := env.ConsensusReactor.WaitSync() 216 if nodeIsSyncing { 217 return env.BlockStore.Height() 218 } 219 return env.BlockStore.Height() + 1 220 }