diff --git a/database/database.go b/database/database.go index f889388..f7a8006 100644 --- a/database/database.go +++ b/database/database.go @@ -252,3 +252,22 @@ func (s *DatabaseService) GetLastDailyBuilderStatsEntry(filterType string) (*Bui err := s.DB.Get(entry, query, filterType) return entry, err } + +func (s *DatabaseService) GetRecentPayloadsForExtraData(extraData []string, limit int) (resp []*TmpPayloadsForExtraDataEntry, err error) { + query := ` + SELECT + DISTINCT ON (slot) slot, extra_data, inserted_at, block_timestamp + FROM ` + vars.TableDataAPIPayloadDelivered + ` + wHERE extra_data IN (?) + ORDER BY slot DESC + LIMIT ?;` + + // See also https://jmoiron.github.io/sqlx/#inQueries + query, args, err := sqlx.In(query, extraData, limit) + if err != nil { + return nil, err + } + query = s.DB.Rebind(query) + err = s.DB.Select(&resp, query, args...) + return resp, err +} diff --git a/database/types.go b/database/types.go index bddbdbe..a00ba9a 100644 --- a/database/types.go +++ b/database/types.go @@ -5,6 +5,11 @@ import ( "time" ) +var ( + BuilderStatsEntryTypeExtraData = "extra_data" + BuilderStatsEntryTypeBuilderPubkey = "builder_pubkey" +) + func NewNullBool(b bool) sql.NullBool { return sql.NullBool{ Bool: b, @@ -184,8 +189,9 @@ type BuilderStatsEntry struct { BuilderPubkeys string `db:"builder_pubkeys" json:"builder_pubkeys"` BlocksIncluded int `db:"blocks_included" json:"blocks_included"` } - -var ( - BuilderStatsEntryTypeExtraData = "extra_data" - BuilderStatsEntryTypeBuilderPubkey = "builder_pubkey" -) +type TmpPayloadsForExtraDataEntry struct { + Slot uint64 `db:"slot"` + ExtraData string `db:"extra_data"` + InsertedAt time.Time `db:"inserted_at"` + BlockTimestamp sql.NullTime `db:"block_timestamp"` +} diff --git a/services/website/webserver.go b/services/website/webserver.go index 4c90041..aa39583 100644 --- a/services/website/webserver.go +++ b/services/website/webserver.go @@ -8,6 +8,7 @@ import ( "net/http" _ "net/http/pprof" "os" + "strconv" "strings" "sync" "text/template" @@ -141,6 +142,7 @@ func (srv *Webserver) getRouter() http.Handler { r.HandleFunc("/stats/cowstats", srv.handleCowstatsJSON).Methods(http.MethodGet) r.HandleFunc("/stats/day/{day:[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}}", srv.handleDailyStats).Methods(http.MethodGet) r.HandleFunc("/stats/day/{day:[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}}/json", srv.handleDailyStatsJSON).Methods(http.MethodGet) + r.HandleFunc("/stats/_test/extradata-payloads", srv.handleExtraDataPayloads).Methods(http.MethodGet) r.HandleFunc("/livez", srv.handleLivenessCheck) r.HandleFunc("/healthz", srv.handleHealthCheck) @@ -408,6 +410,67 @@ func (srv *Webserver) handleHealthCheck(w http.ResponseWriter, r *http.Request) srv.RespondOK(w, resp) } +// Return recent payloads for a given extra data string +func (srv *Webserver) handleExtraDataPayloads(w http.ResponseWriter, r *http.Request) { + type apiRespEntry struct { + Slot uint64 `json:"slot"` + SlotTime string `json:"slot_time"` + ExtraData string `json:"extra_data"` + PrevSlot uint64 `json:"prev_slot"` + MinSincePrevSlot uint64 `json:"min_since_prev_slot"` + } + + extraData := r.URL.Query().Get("extra_data") + extraDataSlice := []string{extraData} + if extraData == "fb" { + extraDataSlice = []string{"Illuminate Dmocratize Dstribute", "Illuminate Dmocrtz Dstrib Prtct"} + } + + limit := 20 + limitArg := r.URL.Query().Get("limit") + if limitArg != "" { + l, err := strconv.Atoi(limitArg) + if err != nil { + srv.RespondError(w, http.StatusBadRequest, "invalid limit") + return + } + limit = l + } + + payloads, err := srv.db.GetRecentPayloadsForExtraData(extraDataSlice, limit+1) + if err != nil { + srv.log.WithError(err).Error("error getting payloads") + srv.RespondError(w, http.StatusInternalServerError, "error getting payloads") + return + } + + entries := make([]apiRespEntry, len(payloads)) + var prevSlot uint64 + var prevSlotTime time.Time + + // iterate over payloads in reverse + for i := len(payloads) - 1; i >= 0; i-- { + payload := payloads[i] + slotStartTime := common.SlotToTime(payload.Slot) + entry := apiRespEntry{ + Slot: payload.Slot, + SlotTime: slotStartTime.Format("2006-01-02 15:04:05"), + ExtraData: payload.ExtraData, + } + if prevSlot > 0 { + entry.PrevSlot = prevSlot + entry.MinSincePrevSlot = uint64(slotStartTime.Sub(prevSlotTime).Abs().Minutes()) + } + entries[i] = entry + prevSlotTime = slotStartTime + prevSlot = payload.Slot + } + + // trim last entry + entries = entries[:len(entries)-1] + srv.RespondOK(w, entries) +} + // func (srv *Webserver) updateHTML() { // var err error // srv.log.Info("Updating HTML data...")