|
| 1 | +package v2 |
| 2 | + |
| 3 | +import ( |
| 4 | + "errors" |
| 5 | + "fmt" |
| 6 | + "net/http" |
| 7 | + "time" |
| 8 | + |
| 9 | + v2 "github.com/Layr-Labs/eigenda/disperser/common/v2" |
| 10 | + gethcommon "github.com/ethereum/go-ethereum/common" |
| 11 | + "github.com/gin-gonic/gin" |
| 12 | +) |
| 13 | + |
| 14 | +// FetchAccountBlobFeed godoc |
| 15 | +// |
| 16 | +// @Summary Fetch blobs posted by an account in a time window by specific direction |
| 17 | +// @Tags Accounts |
| 18 | +// @Produce json |
| 19 | +// @Param account_id path string true "The account ID to fetch blob feed for" |
| 20 | +// @Param direction query string false "Direction to fetch: 'forward' (oldest to newest, ASC order) or 'backward' (newest to oldest, DESC order) [default: forward]" |
| 21 | +// @Param before query string false "Fetch blobs before this time, exclusive (ISO 8601 format, example: 2006-01-02T15:04:05Z) [default: now]" |
| 22 | +// @Param after query string false "Fetch blobs after this time, exclusive (ISO 8601 format, example: 2006-01-02T15:04:05Z); must be smaller than `before` [default: `before`-1h]" |
| 23 | +// @Param limit query int false "Maximum number of blobs to return; if limit <= 0 or >1000, it's treated as 1000 [default: 20; max: 1000]" |
| 24 | +// @Success 200 {object} AccountBlobFeedResponse |
| 25 | +// @Failure 400 {object} ErrorResponse "error: Bad request" |
| 26 | +// @Failure 404 {object} ErrorResponse "error: Not found" |
| 27 | +// @Failure 500 {object} ErrorResponse "error: Server error" |
| 28 | +// @Router /accounts/{account_id}/blobs [get] |
| 29 | +func (s *ServerV2) FetchAccountBlobFeed(c *gin.Context) { |
| 30 | + handlerStart := time.Now() |
| 31 | + var err error |
| 32 | + |
| 33 | + // Parse account ID |
| 34 | + accountStr := c.Param("account_id") |
| 35 | + if !gethcommon.IsHexAddress(accountStr) { |
| 36 | + s.metrics.IncrementInvalidArgRequestNum("FetchAccountBlobFeed") |
| 37 | + invalidParamsErrorResponse(c, errors.New("account id is not valid hex")) |
| 38 | + return |
| 39 | + } |
| 40 | + accountId := gethcommon.HexToAddress(accountStr) |
| 41 | + if accountId == (gethcommon.Address{}) { |
| 42 | + s.metrics.IncrementInvalidArgRequestNum("FetchAccountBlobFeed") |
| 43 | + invalidParamsErrorResponse(c, errors.New("zero account id is not valid")) |
| 44 | + return |
| 45 | + } |
| 46 | + |
| 47 | + // Parse the feed params |
| 48 | + params, err := ParseFeedParams(c, s.metrics, "FetchAccountBlobFeed") |
| 49 | + if err != nil { |
| 50 | + s.metrics.IncrementInvalidArgRequestNum("FetchAccountBlobFeed") |
| 51 | + invalidParamsErrorResponse(c, err) |
| 52 | + return |
| 53 | + } |
| 54 | + |
| 55 | + var blobs []*v2.BlobMetadata |
| 56 | + |
| 57 | + if params.direction == "forward" { |
| 58 | + blobs, err = s.blobMetadataStore.GetBlobMetadataByAccountID( |
| 59 | + c.Request.Context(), |
| 60 | + accountId, |
| 61 | + uint64(params.afterTime.UnixNano()), |
| 62 | + uint64(params.beforeTime.UnixNano()), |
| 63 | + params.limit, |
| 64 | + true, // ascending=true |
| 65 | + ) |
| 66 | + } else { |
| 67 | + blobs, err = s.blobMetadataStore.GetBlobMetadataByAccountID( |
| 68 | + c.Request.Context(), |
| 69 | + accountId, |
| 70 | + uint64(params.afterTime.UnixNano()), |
| 71 | + uint64(params.beforeTime.UnixNano()), |
| 72 | + params.limit, |
| 73 | + false, // ascending=false |
| 74 | + ) |
| 75 | + } |
| 76 | + |
| 77 | + if err != nil { |
| 78 | + s.metrics.IncrementFailedRequestNum("FetchAccountBlobFeed") |
| 79 | + errorResponse(c, fmt.Errorf("failed to fetch blobs from blob metadata store for account (%s): %w", accountId.Hex(), err)) |
| 80 | + return |
| 81 | + } |
| 82 | + |
| 83 | + blobInfo := make([]BlobInfo, len(blobs)) |
| 84 | + for i := 0; i < len(blobs); i++ { |
| 85 | + bk, err := blobs[i].BlobHeader.BlobKey() |
| 86 | + if err != nil { |
| 87 | + s.metrics.IncrementFailedRequestNum("FetchAccountBlobFeed") |
| 88 | + errorResponse(c, fmt.Errorf("blob metadata is malformed and failed to serialize blob key: %w", err)) |
| 89 | + return |
| 90 | + } |
| 91 | + blobInfo[i].BlobKey = bk.Hex() |
| 92 | + blobInfo[i].BlobMetadata = blobs[i] |
| 93 | + } |
| 94 | + |
| 95 | + response := &AccountBlobFeedResponse{ |
| 96 | + AccountId: accountId.Hex(), |
| 97 | + Blobs: blobInfo, |
| 98 | + } |
| 99 | + |
| 100 | + s.metrics.IncrementSuccessfulRequestNum("FetchAccountBlobFeed") |
| 101 | + s.metrics.ObserveLatency("FetchAccountBlobFeed", time.Since(handlerStart)) |
| 102 | + c.JSON(http.StatusOK, response) |
| 103 | +} |
0 commit comments