diff --git a/disperser/dataapi/docs/docs.go b/disperser/dataapi/docs/docs.go index bbc26fc7e..247530c88 100644 --- a/disperser/dataapi/docs/docs.go +++ b/disperser/dataapi/docs/docs.go @@ -21,7 +21,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Feed" + "Batch" ], "summary": "Fetch batch by the batch header hash", "parameters": [ @@ -37,7 +37,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/dataapi.BlobResponse" + "$ref": "#/definitions/dataapi.BatchResponse" } }, "400": { @@ -67,7 +67,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Feed" + "Blob" ], "summary": "Fetch blob metadata by blob key", "parameters": [ @@ -107,6 +107,105 @@ const docTemplate = `{ } } }, + "/blobs/{blob_key}/certificate": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Blob" + ], + "summary": "Fetch blob certificate by blob key", + "parameters": [ + { + "type": "string", + "description": "Blob key in hex string", + "name": "blob_key", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dataapi.BlobCertificateResponse" + } + }, + "400": { + "description": "error: Bad request", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + }, + "404": { + "description": "error: Not found", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + }, + "500": { + "description": "error: Server error", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + } + } + } + }, + "/blobs/{blob_key}/verification-info": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Blob" + ], + "summary": "Fetch blob verification info by blob key and batch header hash", + "parameters": [ + { + "type": "string", + "description": "Blob key in hex string", + "name": "blob_key", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Batch header hash in hex string", + "name": "batch_header_hash", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dataapi.BlobVerificationInfoResponse" + } + }, + "400": { + "description": "error: Bad request", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + }, + "404": { + "description": "error: Not found", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + }, + "500": { + "description": "error: Server error", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + } + } + } + }, "/feed/batches/{batch_header_hash}/blobs": { "get": { "produces": [ @@ -1062,6 +1161,25 @@ const docTemplate = `{ "big.Int": { "type": "object" }, + "core.G1Point": { + "type": "object", + "properties": { + "x": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "core.G2Point": { + "type": "object", + "properties": { + "x": { + "$ref": "#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2" + } + } + }, "core.PaymentMetadata": { "type": "object", "properties": { @@ -1107,6 +1225,42 @@ const docTemplate = `{ } } }, + "core.Signature": { + "type": "object", + "properties": { + "x": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "dataapi.BatchResponse": { + "type": "object", + "properties": { + "batch_header_hash": { + "type": "string" + }, + "blob_verification_infos": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_Layr-Labs_eigenda_core_v2.BlobVerificationInfo" + } + }, + "signed_batch": { + "$ref": "#/definitions/dataapi.SignedBatch" + } + } + }, + "dataapi.BlobCertificateResponse": { + "type": "object", + "properties": { + "blob_certificate": { + "$ref": "#/definitions/github_com_Layr-Labs_eigenda_core_v2.BlobCertificate" + } + } + }, "dataapi.BlobMetadataResponse": { "type": "object", "properties": { @@ -1177,6 +1331,14 @@ const docTemplate = `{ } } }, + "dataapi.BlobVerificationInfoResponse": { + "type": "object", + "properties": { + "blob_verification_info": { + "$ref": "#/definitions/github_com_Layr-Labs_eigenda_core_v2.BlobVerificationInfo" + } + } + }, "dataapi.BlobsResponse": { "type": "object", "properties": { @@ -1444,6 +1606,17 @@ const docTemplate = `{ } } }, + "dataapi.SignedBatch": { + "type": "object", + "properties": { + "attestation": { + "$ref": "#/definitions/github_com_Layr-Labs_eigenda_core_v2.Attestation" + }, + "batch_header": { + "$ref": "#/definitions/github_com_Layr-Labs_eigenda_core_v2.BatchHeader" + } + } + }, "dataapi.Throughput": { "type": "object", "properties": { @@ -1499,6 +1672,101 @@ const docTemplate = `{ } } }, + "github_com_Layr-Labs_eigenda_core_v2.Attestation": { + "type": "object", + "properties": { + "apkg2": { + "description": "APKG2 is the aggregate public key of all signers", + "allOf": [ + { + "$ref": "#/definitions/core.G2Point" + } + ] + }, + "attestedAt": { + "description": "AttestedAt is the time the attestation was made", + "type": "integer" + }, + "batchRoot": { + "description": "BatchRoot is the root of a Merkle tree whose leaves are the keys of the blobs in the batch", + "type": "array", + "items": { + "type": "integer" + } + }, + "nonSignerPubKeys": { + "description": "NonSignerPubKeys are the public keys of the operators that did not sign the blob", + "type": "array", + "items": { + "$ref": "#/definitions/core.G1Point" + } + }, + "quorumAPKs": { + "description": "QuorumAPKs is the aggregate public keys of all operators in each quorum", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/core.G1Point" + } + }, + "quorumNumbers": { + "description": "QuorumNumbers contains the quorums relevant for the attestation", + "type": "array", + "items": { + "type": "integer" + } + }, + "quorumResults": { + "description": "QuorumResults contains the results of the quorum verification", + "type": "object", + "additionalProperties": { + "type": "integer" + } + }, + "referenceBlockNumber": { + "description": "ReferenceBlockNumber is the block number at which all operator information (stakes, indexes, etc.) is taken from", + "type": "integer" + }, + "sigma": { + "description": "Sigma is the aggregate signature of all signers", + "allOf": [ + { + "$ref": "#/definitions/core.Signature" + } + ] + } + } + }, + "github_com_Layr-Labs_eigenda_core_v2.BatchHeader": { + "type": "object", + "properties": { + "batchRoot": { + "description": "BatchRoot is the root of a Merkle tree whose leaves are the keys of the blobs in the batch", + "type": "array", + "items": { + "type": "integer" + } + }, + "referenceBlockNumber": { + "description": "ReferenceBlockNumber is the block number at which all operator information (stakes, indexes, etc.) is taken from", + "type": "integer" + } + } + }, + "github_com_Layr-Labs_eigenda_core_v2.BlobCertificate": { + "type": "object", + "properties": { + "blobHeader": { + "$ref": "#/definitions/github_com_Layr-Labs_eigenda_core_v2.BlobHeader" + }, + "relayKeys": { + "description": "RelayKeys", + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "github_com_Layr-Labs_eigenda_core_v2.BlobHeader": { "type": "object", "properties": { @@ -1532,6 +1800,37 @@ const docTemplate = `{ } } }, + "github_com_Layr-Labs_eigenda_core_v2.BlobVerificationInfo": { + "type": "object", + "properties": { + "BlobKey": { + "type": "array", + "items": { + "type": "integer" + } + }, + "batchRoot": { + "description": "BatchRoot is the root of a Merkle tree whose leaves are the keys of the blobs in the batch", + "type": "array", + "items": { + "type": "integer" + } + }, + "blobIndex": { + "type": "integer" + }, + "inclusionProof": { + "type": "array", + "items": { + "type": "integer" + } + }, + "referenceBlockNumber": { + "description": "ReferenceBlockNumber is the block number at which all operator information (stakes, indexes, etc.) is taken from", + "type": "integer" + } + } + }, "github_com_Layr-Labs_eigenda_disperser.BlobStatus": { "type": "integer", "enum": [ diff --git a/disperser/dataapi/docs/swagger.json b/disperser/dataapi/docs/swagger.json index 9f8e09c67..667c5d6a2 100644 --- a/disperser/dataapi/docs/swagger.json +++ b/disperser/dataapi/docs/swagger.json @@ -17,7 +17,7 @@ "application/json" ], "tags": [ - "Feed" + "Batch" ], "summary": "Fetch batch by the batch header hash", "parameters": [ @@ -33,7 +33,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/dataapi.BlobResponse" + "$ref": "#/definitions/dataapi.BatchResponse" } }, "400": { @@ -63,7 +63,7 @@ "application/json" ], "tags": [ - "Feed" + "Blob" ], "summary": "Fetch blob metadata by blob key", "parameters": [ @@ -103,6 +103,105 @@ } } }, + "/blobs/{blob_key}/certificate": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Blob" + ], + "summary": "Fetch blob certificate by blob key", + "parameters": [ + { + "type": "string", + "description": "Blob key in hex string", + "name": "blob_key", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dataapi.BlobCertificateResponse" + } + }, + "400": { + "description": "error: Bad request", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + }, + "404": { + "description": "error: Not found", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + }, + "500": { + "description": "error: Server error", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + } + } + } + }, + "/blobs/{blob_key}/verification-info": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Blob" + ], + "summary": "Fetch blob verification info by blob key and batch header hash", + "parameters": [ + { + "type": "string", + "description": "Blob key in hex string", + "name": "blob_key", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Batch header hash in hex string", + "name": "batch_header_hash", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/dataapi.BlobVerificationInfoResponse" + } + }, + "400": { + "description": "error: Bad request", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + }, + "404": { + "description": "error: Not found", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + }, + "500": { + "description": "error: Server error", + "schema": { + "$ref": "#/definitions/dataapi.ErrorResponse" + } + } + } + } + }, "/feed/batches/{batch_header_hash}/blobs": { "get": { "produces": [ @@ -1058,6 +1157,25 @@ "big.Int": { "type": "object" }, + "core.G1Point": { + "type": "object", + "properties": { + "x": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "core.G2Point": { + "type": "object", + "properties": { + "x": { + "$ref": "#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2" + } + } + }, "core.PaymentMetadata": { "type": "object", "properties": { @@ -1103,6 +1221,42 @@ } } }, + "core.Signature": { + "type": "object", + "properties": { + "x": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "dataapi.BatchResponse": { + "type": "object", + "properties": { + "batch_header_hash": { + "type": "string" + }, + "blob_verification_infos": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_Layr-Labs_eigenda_core_v2.BlobVerificationInfo" + } + }, + "signed_batch": { + "$ref": "#/definitions/dataapi.SignedBatch" + } + } + }, + "dataapi.BlobCertificateResponse": { + "type": "object", + "properties": { + "blob_certificate": { + "$ref": "#/definitions/github_com_Layr-Labs_eigenda_core_v2.BlobCertificate" + } + } + }, "dataapi.BlobMetadataResponse": { "type": "object", "properties": { @@ -1173,6 +1327,14 @@ } } }, + "dataapi.BlobVerificationInfoResponse": { + "type": "object", + "properties": { + "blob_verification_info": { + "$ref": "#/definitions/github_com_Layr-Labs_eigenda_core_v2.BlobVerificationInfo" + } + } + }, "dataapi.BlobsResponse": { "type": "object", "properties": { @@ -1440,6 +1602,17 @@ } } }, + "dataapi.SignedBatch": { + "type": "object", + "properties": { + "attestation": { + "$ref": "#/definitions/github_com_Layr-Labs_eigenda_core_v2.Attestation" + }, + "batch_header": { + "$ref": "#/definitions/github_com_Layr-Labs_eigenda_core_v2.BatchHeader" + } + } + }, "dataapi.Throughput": { "type": "object", "properties": { @@ -1495,6 +1668,101 @@ } } }, + "github_com_Layr-Labs_eigenda_core_v2.Attestation": { + "type": "object", + "properties": { + "apkg2": { + "description": "APKG2 is the aggregate public key of all signers", + "allOf": [ + { + "$ref": "#/definitions/core.G2Point" + } + ] + }, + "attestedAt": { + "description": "AttestedAt is the time the attestation was made", + "type": "integer" + }, + "batchRoot": { + "description": "BatchRoot is the root of a Merkle tree whose leaves are the keys of the blobs in the batch", + "type": "array", + "items": { + "type": "integer" + } + }, + "nonSignerPubKeys": { + "description": "NonSignerPubKeys are the public keys of the operators that did not sign the blob", + "type": "array", + "items": { + "$ref": "#/definitions/core.G1Point" + } + }, + "quorumAPKs": { + "description": "QuorumAPKs is the aggregate public keys of all operators in each quorum", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/core.G1Point" + } + }, + "quorumNumbers": { + "description": "QuorumNumbers contains the quorums relevant for the attestation", + "type": "array", + "items": { + "type": "integer" + } + }, + "quorumResults": { + "description": "QuorumResults contains the results of the quorum verification", + "type": "object", + "additionalProperties": { + "type": "integer" + } + }, + "referenceBlockNumber": { + "description": "ReferenceBlockNumber is the block number at which all operator information (stakes, indexes, etc.) is taken from", + "type": "integer" + }, + "sigma": { + "description": "Sigma is the aggregate signature of all signers", + "allOf": [ + { + "$ref": "#/definitions/core.Signature" + } + ] + } + } + }, + "github_com_Layr-Labs_eigenda_core_v2.BatchHeader": { + "type": "object", + "properties": { + "batchRoot": { + "description": "BatchRoot is the root of a Merkle tree whose leaves are the keys of the blobs in the batch", + "type": "array", + "items": { + "type": "integer" + } + }, + "referenceBlockNumber": { + "description": "ReferenceBlockNumber is the block number at which all operator information (stakes, indexes, etc.) is taken from", + "type": "integer" + } + } + }, + "github_com_Layr-Labs_eigenda_core_v2.BlobCertificate": { + "type": "object", + "properties": { + "blobHeader": { + "$ref": "#/definitions/github_com_Layr-Labs_eigenda_core_v2.BlobHeader" + }, + "relayKeys": { + "description": "RelayKeys", + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "github_com_Layr-Labs_eigenda_core_v2.BlobHeader": { "type": "object", "properties": { @@ -1528,6 +1796,37 @@ } } }, + "github_com_Layr-Labs_eigenda_core_v2.BlobVerificationInfo": { + "type": "object", + "properties": { + "BlobKey": { + "type": "array", + "items": { + "type": "integer" + } + }, + "batchRoot": { + "description": "BatchRoot is the root of a Merkle tree whose leaves are the keys of the blobs in the batch", + "type": "array", + "items": { + "type": "integer" + } + }, + "blobIndex": { + "type": "integer" + }, + "inclusionProof": { + "type": "array", + "items": { + "type": "integer" + } + }, + "referenceBlockNumber": { + "description": "ReferenceBlockNumber is the block number at which all operator information (stakes, indexes, etc.) is taken from", + "type": "integer" + } + } + }, "github_com_Layr-Labs_eigenda_disperser.BlobStatus": { "type": "integer", "enum": [ diff --git a/disperser/dataapi/docs/swagger.yaml b/disperser/dataapi/docs/swagger.yaml index c479f789d..019c00ef4 100644 --- a/disperser/dataapi/docs/swagger.yaml +++ b/disperser/dataapi/docs/swagger.yaml @@ -1,6 +1,18 @@ definitions: big.Int: type: object + core.G1Point: + properties: + x: + items: + type: integer + type: array + type: object + core.G2Point: + properties: + x: + $ref: '#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2' + type: object core.PaymentMetadata: properties: account_id: @@ -42,6 +54,29 @@ definitions: data was posted to the DA node. type: integer type: object + core.Signature: + properties: + x: + items: + type: integer + type: array + type: object + dataapi.BatchResponse: + properties: + batch_header_hash: + type: string + blob_verification_infos: + items: + $ref: '#/definitions/github_com_Layr-Labs_eigenda_core_v2.BlobVerificationInfo' + type: array + signed_batch: + $ref: '#/definitions/dataapi.SignedBatch' + type: object + dataapi.BlobCertificateResponse: + properties: + blob_certificate: + $ref: '#/definitions/github_com_Layr-Labs_eigenda_core_v2.BlobCertificate' + type: object dataapi.BlobMetadataResponse: properties: batch_header_hash: @@ -88,6 +123,11 @@ definitions: status: type: string type: object + dataapi.BlobVerificationInfoResponse: + properties: + blob_verification_info: + $ref: '#/definitions/github_com_Layr-Labs_eigenda_core_v2.BlobVerificationInfo' + type: object dataapi.BlobsResponse: properties: data: @@ -260,6 +300,13 @@ definitions: meta: $ref: '#/definitions/dataapi.Meta' type: object + dataapi.SignedBatch: + properties: + attestation: + $ref: '#/definitions/github_com_Layr-Labs_eigenda_core_v2.Attestation' + batch_header: + $ref: '#/definitions/github_com_Layr-Labs_eigenda_core_v2.BatchHeader' + type: object dataapi.Throughput: properties: throughput: @@ -295,6 +342,75 @@ definitions: x: $ref: '#/definitions/github_com_consensys_gnark-crypto_ecc_bn254_internal_fptower.E2' type: object + github_com_Layr-Labs_eigenda_core_v2.Attestation: + properties: + apkg2: + allOf: + - $ref: '#/definitions/core.G2Point' + description: APKG2 is the aggregate public key of all signers + attestedAt: + description: AttestedAt is the time the attestation was made + type: integer + batchRoot: + description: BatchRoot is the root of a Merkle tree whose leaves are the keys + of the blobs in the batch + items: + type: integer + type: array + nonSignerPubKeys: + description: NonSignerPubKeys are the public keys of the operators that did + not sign the blob + items: + $ref: '#/definitions/core.G1Point' + type: array + quorumAPKs: + additionalProperties: + $ref: '#/definitions/core.G1Point' + description: QuorumAPKs is the aggregate public keys of all operators in each + quorum + type: object + quorumNumbers: + description: QuorumNumbers contains the quorums relevant for the attestation + items: + type: integer + type: array + quorumResults: + additionalProperties: + type: integer + description: QuorumResults contains the results of the quorum verification + type: object + referenceBlockNumber: + description: ReferenceBlockNumber is the block number at which all operator + information (stakes, indexes, etc.) is taken from + type: integer + sigma: + allOf: + - $ref: '#/definitions/core.Signature' + description: Sigma is the aggregate signature of all signers + type: object + github_com_Layr-Labs_eigenda_core_v2.BatchHeader: + properties: + batchRoot: + description: BatchRoot is the root of a Merkle tree whose leaves are the keys + of the blobs in the batch + items: + type: integer + type: array + referenceBlockNumber: + description: ReferenceBlockNumber is the block number at which all operator + information (stakes, indexes, etc.) is taken from + type: integer + type: object + github_com_Layr-Labs_eigenda_core_v2.BlobCertificate: + properties: + blobHeader: + $ref: '#/definitions/github_com_Layr-Labs_eigenda_core_v2.BlobHeader' + relayKeys: + description: RelayKeys + items: + type: integer + type: array + type: object github_com_Layr-Labs_eigenda_core_v2.BlobHeader: properties: blobCommitments: @@ -317,6 +433,29 @@ definitions: type: integer type: array type: object + github_com_Layr-Labs_eigenda_core_v2.BlobVerificationInfo: + properties: + BlobKey: + items: + type: integer + type: array + batchRoot: + description: BatchRoot is the root of a Merkle tree whose leaves are the keys + of the blobs in the batch + items: + type: integer + type: array + blobIndex: + type: integer + inclusionProof: + items: + type: integer + type: array + referenceBlockNumber: + description: ReferenceBlockNumber is the block number at which all operator + information (stakes, indexes, etc.) is taken from + type: integer + type: object github_com_Layr-Labs_eigenda_disperser.BlobStatus: enum: - 0 @@ -375,7 +514,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/dataapi.BlobResponse' + $ref: '#/definitions/dataapi.BatchResponse' "400": description: 'error: Bad request' schema: @@ -390,7 +529,7 @@ paths: $ref: '#/definitions/dataapi.ErrorResponse' summary: Fetch batch by the batch header hash tags: - - Feed + - Batch /blobs/{blob_key}: get: parameters: @@ -420,7 +559,72 @@ paths: $ref: '#/definitions/dataapi.ErrorResponse' summary: Fetch blob metadata by blob key tags: - - Feed + - Blob + /blobs/{blob_key}/certificate: + get: + parameters: + - description: Blob key in hex string + in: path + name: blob_key + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dataapi.BlobCertificateResponse' + "400": + description: 'error: Bad request' + schema: + $ref: '#/definitions/dataapi.ErrorResponse' + "404": + description: 'error: Not found' + schema: + $ref: '#/definitions/dataapi.ErrorResponse' + "500": + description: 'error: Server error' + schema: + $ref: '#/definitions/dataapi.ErrorResponse' + summary: Fetch blob certificate by blob key + tags: + - Blob + /blobs/{blob_key}/verification-info: + get: + parameters: + - description: Blob key in hex string + in: path + name: blob_key + required: true + type: string + - description: Batch header hash in hex string + in: path + name: batch_header_hash + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/dataapi.BlobVerificationInfoResponse' + "400": + description: 'error: Bad request' + schema: + $ref: '#/definitions/dataapi.ErrorResponse' + "404": + description: 'error: Not found' + schema: + $ref: '#/definitions/dataapi.ErrorResponse' + "500": + description: 'error: Server error' + schema: + $ref: '#/definitions/dataapi.ErrorResponse' + summary: Fetch blob verification info by blob key and batch header hash + tags: + - Blob /feed/batches/{batch_header_hash}/blobs: get: parameters: diff --git a/disperser/dataapi/server_v2.go b/disperser/dataapi/server_v2.go index 741a98ff4..660a91fa4 100644 --- a/disperser/dataapi/server_v2.go +++ b/disperser/dataapi/server_v2.go @@ -35,6 +35,14 @@ type ( BlobSizeBytes uint64 `json:"blob_size_bytes"` } + BlobCertificateResponse struct { + Certificate *corev2.BlobCertificate `json:"blob_certificate"` + } + + BlobVerificationInfoResponse struct { + VerificationInfo *corev2.BlobVerificationInfo `json:"blob_verification_info"` + } + BatchResponse struct { BatchHeaderHash string `json:"batch_header_hash"` SignedBatch *SignedBatch `json:"signed_batch"` @@ -114,6 +122,8 @@ func (s *ServerV2) Start() error { { blob.GET("/blobs/feed", s.FetchBlobFeedHandler) blob.GET("/blobs/:blob_key", s.FetchBlobHandler) + blob.GET("/blobs/:blob_key/certificate", s.FetchBlobCertificateHandler) + blob.GET("/blobs/:blob_key/verification-info", s.FetchBlobVerificationInfoHandler) } batch := v2.Group("/batch") { @@ -180,7 +190,7 @@ func (s *ServerV2) FetchBlobFeedHandler(c *gin.Context) { // FetchBlobHandler godoc // // @Summary Fetch blob metadata by blob key -// @Tags Feed +// @Tags Blob // @Produce json // @Param blob_key path string true "Blob key in hex string" // @Success 200 {object} BlobResponse @@ -214,6 +224,83 @@ func (s *ServerV2) FetchBlobHandler(c *gin.Context) { c.JSON(http.StatusOK, response) } +// FetchBlobCertificateHandler godoc +// +// @Summary Fetch blob certificate by blob key +// @Tags Blob +// @Produce json +// @Param blob_key path string true "Blob key in hex string" +// @Success 200 {object} BlobCertificateResponse +// @Failure 400 {object} ErrorResponse "error: Bad request" +// @Failure 404 {object} ErrorResponse "error: Not found" +// @Failure 500 {object} ErrorResponse "error: Server error" +// @Router /blobs/{blob_key}/certificate [get] +func (s *ServerV2) FetchBlobCertificateHandler(c *gin.Context) { + start := time.Now() + blobKey, err := corev2.HexToBlobKey(c.Param("blob_key")) + if err != nil { + s.metrics.IncrementInvalidArgRequestNum("FetchBlobCertificate") + errorResponse(c, err) + return + } + cert, _, err := s.blobMetadataStore.GetBlobCertificate(c.Request.Context(), blobKey) + if err != nil { + s.metrics.IncrementFailedRequestNum("FetchBlobCertificate") + errorResponse(c, err) + return + } + response := &BlobCertificateResponse{ + Certificate: cert, + } + s.metrics.IncrementSuccessfulRequestNum("FetchBlobCertificate") + s.metrics.ObserveLatency("FetchBlobCertificate", float64(time.Since(start).Milliseconds())) + c.Writer.Header().Set(cacheControlParam, fmt.Sprintf("max-age=%d", maxFeedBlobAge)) + c.JSON(http.StatusOK, response) +} + +// FetchBlobVerificationInfoHandler godoc +// +// @Summary Fetch blob verification info by blob key and batch header hash +// @Tags Blob +// @Produce json +// @Param blob_key path string true "Blob key in hex string" +// @Param batch_header_hash path string true "Batch header hash in hex string" +// +// @Success 200 {object} BlobVerificationInfoResponse +// @Failure 400 {object} ErrorResponse "error: Bad request" +// @Failure 404 {object} ErrorResponse "error: Not found" +// @Failure 500 {object} ErrorResponse "error: Server error" +// @Router /blobs/{blob_key}/verification-info [get] +func (s *ServerV2) FetchBlobVerificationInfoHandler(c *gin.Context) { + start := time.Now() + blobKey, err := corev2.HexToBlobKey(c.Param("blob_key")) + if err != nil { + s.metrics.IncrementInvalidArgRequestNum("FetchBlobVerificationInfo") + errorResponse(c, err) + return + } + batchHeaderHashHex := c.Query("batch_header_hash") + batchHeaderHash, err := ConvertHexadecimalToBytes([]byte(batchHeaderHashHex)) + if err != nil { + s.metrics.IncrementInvalidArgRequestNum("FetchBlobVerificationInfo") + errorResponse(c, err) + return + } + bvi, err := s.blobMetadataStore.GetBlobVerificationInfo(c.Request.Context(), blobKey, batchHeaderHash) + if err != nil { + s.metrics.IncrementFailedRequestNum("FetchBlobVerificationInfo") + errorResponse(c, err) + return + } + response := &BlobVerificationInfoResponse{ + VerificationInfo: bvi, + } + s.metrics.IncrementSuccessfulRequestNum("FetchBlobVerificationInfo") + s.metrics.ObserveLatency("FetchBlobVerificationInfo", float64(time.Since(start).Milliseconds())) + c.Writer.Header().Set(cacheControlParam, fmt.Sprintf("max-age=%d", maxFeedBlobAge)) + c.JSON(http.StatusOK, response) +} + func (s *ServerV2) FetchBatchFeedHandler(c *gin.Context) { errorResponse(c, errors.New("FetchBatchFeedHandler unimplemented")) } @@ -221,10 +308,10 @@ func (s *ServerV2) FetchBatchFeedHandler(c *gin.Context) { // FetchBatchHandler godoc // // @Summary Fetch batch by the batch header hash -// @Tags Feed +// @Tags Batch // @Produce json // @Param batch_header_hash path string true "Batch header hash in hex string" -// @Success 200 {object} BlobResponse +// @Success 200 {object} BatchResponse // @Failure 400 {object} ErrorResponse "error: Bad request" // @Failure 404 {object} ErrorResponse "error: Not found" // @Failure 500 {object} ErrorResponse "error: Server error" diff --git a/disperser/dataapi/server_v2_test.go b/disperser/dataapi/server_v2_test.go index e3ce17a4f..033e7f727 100644 --- a/disperser/dataapi/server_v2_test.go +++ b/disperser/dataapi/server_v2_test.go @@ -198,6 +198,92 @@ func TestFetchBlobHandlerV2(t *testing.T) { assert.Equal(t, blobHeader.PaymentMetadata.CumulativePayment, response.BlobHeader.PaymentMetadata.CumulativePayment) } +func TestFetchBlobCertificateHandler(t *testing.T) { + r := setUpRouter() + + // Set up blob certificate in metadata store + blobHeader := makeBlobHeaderV2(t) + blobKey, err := blobHeader.BlobKey() + require.NoError(t, err) + blobCert := &corev2.BlobCertificate{ + BlobHeader: blobHeader, + RelayKeys: []corev2.RelayKey{0, 2, 4}, + } + fragmentInfo := &encoding.FragmentInfo{ + TotalChunkSizeBytes: 100, + FragmentSizeBytes: 1024 * 1024 * 4, + } + err = blobMetadataStore.PutBlobCertificate(context.Background(), blobCert, fragmentInfo) + require.NoError(t, err) + + r.GET("/v2/blobs/:blob_key/certificate", testDataApiServerV2.FetchBlobCertificateHandler) + w := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/v2/blobs/"+blobKey.Hex()+"/certificate", nil) + r.ServeHTTP(w, req) + res := w.Result() + defer res.Body.Close() + data, err := io.ReadAll(res.Body) + assert.NoError(t, err) + + var response dataapi.BlobCertificateResponse + err = json.Unmarshal(data, &response) + assert.NoError(t, err) + assert.NotNil(t, response) + + assert.Equal(t, http.StatusOK, res.StatusCode) + assert.Equal(t, blobCert.RelayKeys, response.Certificate.RelayKeys) + assert.Equal(t, uint16(0), response.Certificate.BlobHeader.BlobVersion) + assert.Equal(t, blobHeader.Signature, response.Certificate.BlobHeader.Signature) +} + +func TestFetchBlobVerificationInfoHandler(t *testing.T) { + r := setUpRouter() + + // Set up blob verification info in metadata store + blobHeader := makeBlobHeaderV2(t) + blobKey, err := blobHeader.BlobKey() + require.NoError(t, err) + + batchHeader := &corev2.BatchHeader{ + BatchRoot: [32]byte{1, 2, 3}, + ReferenceBlockNumber: 100, + } + batchHeaderHash, err := batchHeader.Hash() + require.NoError(t, err) + fmt.Println("XXt batchHeaderHash bytes:", batchHeaderHash) + fmt.Println("XXt batchHeaderHash hex:", hex.EncodeToString(batchHeaderHash[:])) + + ctx := context.Background() + err = blobMetadataStore.PutBatchHeader(ctx, batchHeader) + require.NoError(t, err) + verificationInfo := &corev2.BlobVerificationInfo{ + BatchHeader: batchHeader, + BlobKey: blobKey, + BlobIndex: 123, + InclusionProof: []byte("inclusion proof"), + } + err = blobMetadataStore.PutBlobVerificationInfo(ctx, verificationInfo) + require.NoError(t, err) + + r.GET("/v2/blobs/:blob_key/verification-info", testDataApiServerV2.FetchBlobVerificationInfoHandler) + w := httptest.NewRecorder() + reqStr := fmt.Sprintf("/v2/blobs/%s/verification-info?batch_header_hash=%s", blobKey.Hex(), hex.EncodeToString(batchHeaderHash[:])) + req := httptest.NewRequest(http.MethodGet, reqStr, nil) + r.ServeHTTP(w, req) + res := w.Result() + defer res.Body.Close() + data, err := io.ReadAll(res.Body) + assert.NoError(t, err) + + var response dataapi.BlobVerificationInfoResponse + err = json.Unmarshal(data, &response) + assert.NoError(t, err) + assert.NotNil(t, response) + + assert.Equal(t, http.StatusOK, res.StatusCode) + assert.Equal(t, verificationInfo.InclusionProof, response.VerificationInfo.InclusionProof) +} + func TestFetchBatchHandlerV2(t *testing.T) { r := setUpRouter()