Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Tvl #330

Merged
merged 18 commits into from
Jan 17, 2025
33 changes: 31 additions & 2 deletions .run/API.run.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,37 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="API" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="celestia-indexer" />
<working_directory value="$PROJECT_DIR$" />
<parameters value="-c configs/dipdup.yml" />
<working_directory value="$PROJECT_DIR$/cmd/api" />
<parameters value="-c ../../configs/dipdup.yml" />
<envs>
<env name="API_PROMETHEUS_ENABLED" value="false" />
<env name="API_RATE_LIMIT" value="20" />
<env name="API_REQUEST_TIMEOUT" value="10" />
<env name="API_WEBSOCKET_ENABLED" value="true" />
<env name="BINANCE_API_RPS" value="5" />
<env name="BINANCE_API_TIMEOUT" value="10" />
<env name="BINANCE_API_URL" value="https://api.binance.com/" />
<env name="CELENIUM_ENV" value="production" />
<env name="CELESTIA_DAL_API_RPS" value="10" />
<env name="CELESTIA_DAL_API_TIMEOUT" value="30 # seconds" />
<env name="CELESTIA_DAL_API_URL" value="" />
<env name="CELESTIA_NODE_AUTH_TOKEN" value="" />
<env name="CELESTIA_NODE_RPS" value="5" />
<env name="CELESTIA_NODE_TIMEOUT" value="10 # seconds" />
<env name="CELESTIA_NODE_URL" value="https://node.celenium.io" />
<env name="CELESTIA_NODE_WS_URL" value="https://node.celenium.io:443" />
<env name="INDEXER_BLOCK_PERIOD" value="15 # seconds" />
<env name="INDEXER_NAME" value="celestia_indexer" />
<env name="INDEXER_SCRIPTS_DIR" value="./../../database" />
<env name="INDEXER_START_LEVEL" value="1" />
<env name="INDEXER_THREADS_COUNT" value="10" />
<env name="LOG_LEVEL" value="debug" />
<env name="POSTGRES_DB" value="celestia" />
<env name="POSTGRES_HOST" value="localhost" />
<env name="POSTGRES_PASSWORD" value="changeme" />
<env name="POSTGRES_PORT" value="5432" />
<env name="POSTGRES_USER" value="dipdup" />
</envs>
<EXTENSION ID="net.ashald.envfile">
<option name="IS_ENABLED" value="true" />
<option name="IS_SUBST" value="false" />
Expand Down
29 changes: 29 additions & 0 deletions .run/Indexer.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,35 @@
<module name="celestia-indexer" />
<working_directory value="$PROJECT_DIR$" />
<parameters value="-c configs/dipdup.yml" />
<envs>
<env name="API_PROMETHEUS_ENABLED" value="false" />
<env name="API_RATE_LIMIT" value="20" />
<env name="API_REQUEST_TIMEOUT" value="10" />
<env name="API_WEBSOCKET_ENABLED" value="true" />
<env name="BINANCE_API_RPS" value="5" />
<env name="BINANCE_API_TIMEOUT" value="10" />
<env name="BINANCE_API_URL" value="https://api.binance.com/" />
<env name="CELENIUM_ENV" value="production" />
<env name="CELESTIA_DAL_API_RPS" value="10" />
<env name="CELESTIA_DAL_API_TIMEOUT" value="30 # seconds" />
<env name="CELESTIA_DAL_API_URL" value="" />
<env name="CELESTIA_NODE_AUTH_TOKEN" value="" />
<env name="CELESTIA_NODE_RPS" value="5" />
<env name="CELESTIA_NODE_TIMEOUT" value="10 # seconds" />
<env name="CELESTIA_NODE_URL" value="https://node.celenium.io" />
<env name="CELESTIA_NODE_WS_URL" value="https://node.celenium.io:443" />
<env name="INDEXER_BLOCK_PERIOD" value="15 # seconds" />
<env name="INDEXER_NAME" value="celestia_indexer" />
<env name="INDEXER_SCRIPTS_DIR" value="./database" />
<env name="INDEXER_START_LEVEL" value="1" />
<env name="INDEXER_THREADS_COUNT" value="10" />
<env name="LOG_LEVEL" value="debug" />
<env name="POSTGRES_DB" value="celestia" />
<env name="POSTGRES_HOST" value="localhost" />
<env name="POSTGRES_PASSWORD" value="changeme" />
<env name="POSTGRES_PORT" value="5432" />
<env name="POSTGRES_USER" value="dipdup" />
</envs>
<EXTENSION ID="net.ashald.envfile">
<option name="IS_ENABLED" value="true" />
<option name="IS_SUBST" value="false" />
Expand Down
31 changes: 21 additions & 10 deletions cmd/api/handler/rollup.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ func (handler RollupHandler) GetBlobs(c echo.Context) error {
type rollupStatsRequest struct {
Id uint64 `example:"1" param:"id" swaggertype:"integer" validate:"required,min=1"`
Timeframe string `example:"hour" param:"timeframe" swaggertype:"string" validate:"required,oneof=hour day month"`
SeriesName string `example:"tps" param:"name" swaggertype:"string" validate:"required,oneof=blobs_count size size_per_blob fee"`
SeriesName string `example:"tps" param:"name" swaggertype:"string" validate:"required,oneof=blobs_count size size_per_blob fee tvl"`
From int64 `example:"1692892095" query:"from" swaggertype:"integer" validate:"omitempty,min=1"`
To int64 `example:"1692892095" query:"to" swaggertype:"integer" validate:"omitempty,min=1"`
}
Expand All @@ -347,7 +347,7 @@ type rollupStatsRequest struct {
// @Tags rollup
// @ID get-rollup-stats
// @Param id path integer true "Internal identity" mininum(1)
// @Param name path string true "Series name" Enums(blobs_count, size, size_per_blob, fee)
// @Param name path string true "Series name" Enums(blobs_count, size, size_per_blob, fee, tvl)
// @Param timeframe path string true "Timeframe" Enums(hour, day, month)
// @Param from query integer false "Time from in unix timestamp" mininum(1)
// @Param to query integer false "Time to in unix timestamp" mininum(1)
Expand All @@ -362,13 +362,24 @@ func (handler RollupHandler) Stats(c echo.Context) error {
return badRequestError(c, err)
}

histogram, err := handler.rollups.Series(
c.Request().Context(),
req.Id,
req.Timeframe,
req.SeriesName,
storage.NewSeriesRequest(req.From, req.To),
)
var histogram []storage.HistogramItem
if req.SeriesName == "tvl" {
histogram, err = handler.rollups.Tvl(
c.Request().Context(),
req.Id,
storage.Timeframe(req.Timeframe),
storage.NewSeriesRequest(req.From, req.To),
)
} else {
histogram, err = handler.rollups.Series(
c.Request().Context(),
req.Id,
storage.Timeframe(req.Timeframe),
req.SeriesName,
storage.NewSeriesRequest(req.From, req.To),
)
}

if err != nil {
return handleError(c, err, handler.rollups)
}
Expand Down Expand Up @@ -485,7 +496,7 @@ func (handler RollupHandler) Distribution(c echo.Context) error {
c.Request().Context(),
req.Id,
req.SeriesName,
req.Timeframe,
storage.Timeframe(req.Timeframe),
)
if err != nil {
return handleError(c, err, handler.rollups)
Expand Down
39 changes: 35 additions & 4 deletions cmd/api/handler/rollup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,15 +319,46 @@ func (s *RollupTestSuite) TestGetBlobs() {
s.Require().Len(logs, 1)
}

func (s *RollupTestSuite) TestTvl() {
name := "tvl"
for _, tf := range []storage.Timeframe{storage.TimeframeDay, storage.TimeframeMonth} {
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := s.echo.NewContext(req, rec)
c.SetPath("/rollup/:id/stats/:name/:timeframe")
c.SetParamNames("id", "name", "timeframe")
c.SetParamValues("1", name, string(tf))

s.rollups.EXPECT().
Tvl(gomock.Any(), uint64(1), tf, storage.NewSeriesRequest(0, 0)).
Return([]storage.HistogramItem{
{
Value: "12345678",
Time: testTime,
},
}, nil)

s.Require().NoError(s.handler.Stats(c))
s.Require().Equal(http.StatusOK, rec.Code)

var histogram []responses.HistogramItem
err := json.NewDecoder(rec.Body).Decode(&histogram)
s.Require().NoError(err)
s.Require().Len(histogram, 1)
s.Require().EqualValues("12345678", histogram[0].Value)
s.Require().EqualValues(testTime, histogram[0].Time)
}
}

func (s *RollupTestSuite) TestStats() {
for _, name := range []string{"blobs_count", "size", "size_per_blob", "fee"} {
for _, tf := range []string{"hour", "day", "month"} {
for _, tf := range []storage.Timeframe{storage.TimeframeHour, storage.TimeframeDay, storage.TimeframeMonth} {
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := s.echo.NewContext(req, rec)
c.SetPath("/rollup/:id/stats/:name/:timeframe")
c.SetParamNames("id", "name", "timeframe")
c.SetParamValues("1", name, tf)
c.SetParamValues("1", name, string(tf))

s.rollups.EXPECT().
Series(gomock.Any(), uint64(1), tf, name, storage.NewSeriesRequest(0, 0)).
Expand All @@ -351,13 +382,13 @@ func (s *RollupTestSuite) TestStats() {

func (s *RollupTestSuite) TestDistribution() {
for _, name := range []string{"blobs_count", "size", "size_per_blob", "fee_per_blob"} {
for _, tf := range []string{"hour", "day"} {
for _, tf := range []storage.Timeframe{storage.TimeframeHour, storage.TimeframeDay} {
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := s.echo.NewContext(req, rec)
c.SetPath("/rollup/:id/distribution/:name/:timeframe")
c.SetParamNames("id", "name", "timeframe")
c.SetParamValues("1", name, tf)
c.SetParamValues("1", name, string(tf))

s.rollups.EXPECT().
Distribution(gomock.Any(), uint64(1), name, tf).
Expand Down
55 changes: 55 additions & 0 deletions cmd/api/handler/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,3 +544,58 @@ func (sh StatsHandler) MessagesCount24h(c echo.Context) error {
}
return returnArray(c, response)
}

// Tvs godoc
//
// @Summary Get TVS of the network
// @Description Get TVS of the network
// @Tags stats
// @ID stats-tvs
// @Produce json
// @Success 200 {integer} float64
// @Failure 500 {object} Error
// @Router /stats/tvs [get]
func (sh StatsHandler) Tvs(c echo.Context) error {
tvs, err := sh.repo.Tvs(c.Request().Context())
if err != nil {
return handleError(c, err, sh.nsRepo)
}
return c.JSON(http.StatusOK, tvs)
}

// TvsSeries godoc
//
// @Summary Get histogram for network TVS
// @Description Get histogram for network TVS by timeframe
// @Tags stats
// @ID stats-tvs-series
// @Param timeframe path string true "Timeframe" Enums(day, month)
// @Produce json
// @Success 200 {array} responses.SeriesItem
// @Failure 400 {object} Error
// @Failure 500 {object} Error
// @Router /stats/tvs/{timeframe} [get]
func (sh StatsHandler) TvsSeries(c echo.Context) error {
req, err := bindAndValidate[tvsSeriesRequest](c)
if err != nil {
return badRequestError(c, err)
}

tvs, err := sh.repo.TvsSeries(c.Request().Context(), storage.Timeframe(req.Timeframe))
if err != nil {
return handleError(c, err, sh.nsRepo)
}
if len(tvs) == 0 {
return c.JSON(http.StatusOK, []any{})
}

response := make([]responses.SeriesItem, len(tvs))
for i := range tvs {
response[i] = responses.NewSeriesItem(tvs[i])
}
return returnArray(c, response)
}

type tvsSeriesRequest struct {
Timeframe string `example:"hour" param:"timeframe" swaggertype:"string" validate:"required,oneof=day month"`
}
62 changes: 62 additions & 0 deletions cmd/api/handler/stats_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -583,3 +583,65 @@ func (s *StatsTestSuite) TestMessgaesCount24h() {
s.Require().EqualValues("test", response[0].Name)
s.Require().EqualValues(100, response[0].Value)
}

func (s *StatsTestSuite) TestTvs() {
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := s.echo.NewContext(req, rec)
c.SetPath("/v1/stats/tvs")

s.stats.EXPECT().
Tvs(gomock.Any()).
Return(12345678.90, nil)

s.Require().NoError(s.handler.Tvs(c))
s.Require().Equal(http.StatusOK, rec.Code)

var response float64
err := json.NewDecoder(rec.Body).Decode(&response)
s.Require().NoError(err)
s.Require().Equal(12345678.90, response)
}

func (s *StatsTestSuite) TestTvsSeries() {
for _, tf := range []storage.Timeframe{
storage.TimeframeDay,
storage.TimeframeMonth,
} {
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := s.echo.NewContext(req, rec)
c.SetPath("/v1/stats/tvs/:timeframe")
c.SetParamNames("timeframe")
c.SetParamValues(string(tf))

s.stats.EXPECT().
TvsSeries(gomock.Any(), tf).
Return([]storage.SeriesItem{
{
Time: testTime,
Value: "12345678",
},
{
Time: testTime,
Value: "87654321",
},
}, nil)

s.Require().NoError(s.handler.TvsSeries(c))
s.Require().Equal(http.StatusOK, rec.Code)

var response []responses.SeriesItem
err := json.NewDecoder(rec.Body).Decode(&response)
s.Require().NoError(err)
s.Require().Len(response, 2)

item1 := response[0]
s.Require().Equal("12345678", item1.Value)
s.Require().Equal(testTime, item1.Time)

item2 := response[1]
s.Require().Equal("87654321", item2.Value)
s.Require().Equal(testTime, item2.Time)
}
}
2 changes: 2 additions & 0 deletions cmd/api/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,8 @@ func initHandlers(ctx context.Context, e *echo.Echo, cfg Config, db postgres.Sto
stats.GET("/rollup_stats_24h", statsHandler.RollupStats24h)
stats.GET("/square_size", statsHandler.SquareSize)
stats.GET("/messages_count_24h", statsHandler.MessagesCount24h)
stats.GET("/tvs", statsHandler.Tvs)
stats.GET("/tvs/:timeframe", statsHandler.TvsSeries)

price := stats.Group("/price")
{
Expand Down
2 changes: 2 additions & 0 deletions cmd/api/routes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ func TestRoutes(t *testing.T) {
"/v1/rollup/stats/series GET": {},
"/v1/rollup/group GET": {},
"/v1/blob/proofs POST": {},
"/v1/stats/tvs GET": {},
"/v1/stats/tvs/:timeframe GET": {},
}

ctx, cancel := context.WithCancel(context.Background())
Expand Down
Loading
Loading