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  }